diff --git a/src/objects/api/v2/filters.py b/src/objects/api/v2/filters.py index f962787c..6ed36484 100644 --- a/src/objects/api/v2/filters.py +++ b/src/objects/api/v2/filters.py @@ -27,6 +27,47 @@ """ +DATA_ATTRS_HELP_TEXT = ( + _( + """**DEPRECATED: Use 'data_attr' instead**. +Only include objects that have attributes with certain values. +Data filtering expressions are comma-separated and are structured as follows: + +%(value_part_help_text)s + +Example: in order to display only objects with `height` equal to 100, query `data_attrs=height__exact__100` +should be used. If `height` is nested inside `dimensions` attribute, query should look like +`data_attrs=dimensions__height__exact__100` + +`value` may not contain comma, since commas are used as separator between filtering expressions. +If you want to use commas in `value` you can use `data_attr` query parameter. +""" + ) + % {"value_part_help_text": DATA_ATTR_VALUE_HELP_TEXT} +) + +DATA_ATTR_HELP_TEXT = ( + _( + """Only include objects that have attributes with certain values. + +%(value_part_help_text)s + +Example: in order to display only objects with `height` equal to 100, query `data_attr=height__exact__100` +should be used. If `height` is nested inside `dimensions` attribute, query should look like +`data_attr=dimensions__height__exact__100` + +This filter is very similar to the old `data_attrs` filter, but it has two differences: + +* `value` may contain commas +* only one filtering expression is allowed + +If you want to use several filtering expressions, just use this `data_attr` several times in the query string. +Example: `data_attr=height__exact__100&data_attr=naam__icontains__boom` +""" + ) + % {"value_part_help_text": DATA_ATTR_VALUE_HELP_TEXT} +) + def filter_data_attr_value_part(value_part: str, queryset: QuerySet) -> QuerySet: """ @@ -98,49 +139,17 @@ class ObjectRecordFilterSet(FilterSet): "date would be between `registrationAt` attributes of different records" ), ) + data_attrs = filters.CharFilter( method="filter_data_attrs", validators=[validate_data_attrs], - help_text=_( - """**DEPRECATED: Use 'data_attr' instead**. -Only include objects that have attributes with certain values. -Data filtering expressions are comma-separated and are structured as follows: - -%(value_part_help_text)s - -Example: in order to display only objects with `height` equal to 100, query `data_attrs=height__exact__100` -should be used. If `height` is nested inside `dimensions` attribute, query should look like -`data_attrs=dimensions__height__exact__100` - -`value` may not contain comma, since commas are used as separator between filtering expressions. -If you want to use commas in `value` you can use `data_attr` query parameter. -""" - ) - % {"value_part_help_text": DATA_ATTR_VALUE_HELP_TEXT}, + help_text=DATA_ATTRS_HELP_TEXT, ) data_attr = ManyCharFilter( method="filter_data_attr", validators=[validate_data_attr], - help_text=_( - """Only include objects that have attributes with certain values. - -%(value_part_help_text)s - -Example: in order to display only objects with `height` equal to 100, query `data_attr=height__exact__100` -should be used. If `height` is nested inside `dimensions` attribute, query should look like -`data_attr=dimensions__height__exact__100` - -This filter is very similar to the old `data_attrs` filter, but it has two differences: - -* `value` may contain commas -* only one filtering expression is allowed - -If you want to use several filtering expressions, just use this `data_attr` several times in the query string. -Example: `data_attr=height__exact__100&data_attr=naam__icontains__boom` -""" - ) - % {"value_part_help_text": DATA_ATTR_VALUE_HELP_TEXT}, + help_text=DATA_ATTR_HELP_TEXT, ) data_icontains = filters.CharFilter( diff --git a/src/objects/api/v2/openapi.yaml b/src/objects/api/v2/openapi.yaml index 13bd3b3b..7fa40c26 100644 --- a/src/objects/api/v2/openapi.yaml +++ b/src/objects/api/v2/openapi.yaml @@ -124,6 +124,7 @@ paths: If you want to use several filtering expressions, just use this `data_attr` several times in the query string. Example: `data_attr=height__exact__100&data_attr=naam__icontains__boom` + explode: true - in: query name: data_attrs schema: @@ -157,6 +158,7 @@ paths: `value` may not contain comma, since commas are used as separator between filtering expressions. If you want to use commas in `value` you can use `data_attr` query parameter. + deprecated: true - in: query name: data_icontains schema: diff --git a/src/objects/api/v2/views.py b/src/objects/api/v2/views.py index 9a73f420..ab2e4245 100644 --- a/src/objects/api/v2/views.py +++ b/src/objects/api/v2/views.py @@ -4,8 +4,12 @@ from django.db import models from django.utils.dateparse import parse_date -from drf_spectacular.types import OpenApiTypes -from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_view +from drf_spectacular.utils import ( + OpenApiParameter, + OpenApiTypes, + extend_schema, + extend_schema_view, +) from rest_framework import mixins, viewsets from rest_framework.decorators import action from rest_framework.generics import get_object_or_404 @@ -28,13 +32,32 @@ PermissionSerializer, ) from ..utils import is_date -from .filters import ObjectRecordFilterSet +from .filters import DATA_ATTR_HELP_TEXT, DATA_ATTRS_HELP_TEXT, ObjectRecordFilterSet + +# manually override OAS because of "deprecated" attribute +data_attrs_parameter = OpenApiParameter( + name="data_attrs", + type=OpenApiTypes.STR, + location=OpenApiParameter.QUERY, + description=DATA_ATTRS_HELP_TEXT, + deprecated=True, +) + +# manually override OAS because of "explode" attribute +data_attr_parameter = OpenApiParameter( + name="data_attr", + location=OpenApiParameter.QUERY, + type=OpenApiTypes.STR, + description=DATA_ATTR_HELP_TEXT, + explode=True, +) @extend_schema_view( list=extend_schema( description="Retrieve a list of OBJECTs and their actual RECORD. " - "The actual record is defined as if the query parameter `date=` was given." + "The actual record is defined as if the query parameter `date=` was given.", + parameters=[data_attrs_parameter, data_attr_parameter], ), retrieve=extend_schema( description="Retrieve a single OBJECT and its actual RECORD. " diff --git a/src/objects/api/validators.py b/src/objects/api/validators.py index 68f25593..f0d49797 100644 --- a/src/objects/api/validators.py +++ b/src/objects/api/validators.py @@ -110,7 +110,8 @@ def validate_data_attr(value: list): # check that comma can be only in the value part if "," in value_part.rsplit("__", 1)[0]: message = _( - "Filter expression '%(value_part)s' doesn't have the shape 'key__operator__value'" + "Filter expression '%(value_part)s' must have the shape 'key__operator__value', " + "commas can only be present in the 'value'" ) % {"value_part": value_part} raise serializers.ValidationError(message, code=code) diff --git a/src/objects/tests/v2/test_filters.py b/src/objects/tests/v2/test_filters.py index 9c3e2318..4b30c381 100644 --- a/src/objects/tests/v2/test_filters.py +++ b/src/objects/tests/v2/test_filters.py @@ -492,6 +492,10 @@ def test_filter_exact_date(self): record = ObjectRecordFactory.create( data={"date": "2000-11-01"}, object__object_type=self.object_type ) + ObjectRecordFactory.create( + data={"date": "2020-11-01"}, object__object_type=self.object_type + ) + ObjectRecordFactory.create(data={}, object__object_type=self.object_type) response = self.client.get(self.url, {"data_attr": "date__exact__2000-11-01"}) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -814,7 +818,8 @@ def test_filter_comma_separated_invalid(self): response.json(), [ "Filter expression 'dimensions__diameter__exact__4,name__exact__demo' " - "doesn't have the shape 'key__operator__value'" + "must have the shape 'key__operator__value', commas can only be present in " + "the 'value'" ], )