Skip to content

Commit

Permalink
also allow @extend_schema_field on django-filter filter method #660
Browse files Browse the repository at this point in the history
  • Loading branch information
tfranzel committed Feb 17, 2022
1 parent 5da99c5 commit 49bb6bf
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 6 deletions.
21 changes: 15 additions & 6 deletions drf_spectacular/contrib/django_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,16 @@ def resolve_filter_field(self, auto_schema, model, filterset_class, field_name,
filters.IsoDateTimeFromToRangeFilter: OpenApiTypes.DATETIME,
filters.DateTimeFromToRangeFilter: OpenApiTypes.DATETIME,
}
if has_override(filter_field, 'field'):
annotation = get_override(filter_field, 'field')
filter_method = self._get_filter_method(filterset_class, filter_field)

if has_override(filter_field, 'field') or has_override(filter_method, 'field'):
annotation = (
get_override(filter_field, 'field') or get_override(filter_method, 'field')
)
if is_basic_type(annotation):
schema = build_basic_type(annotation)
else:
# allow injecting raw schema via @extend_schema_field decorator
schema = annotation
elif isinstance(filter_field, tuple(unambiguous_mapping)):
for cls in filter_field.__class__.__mro__:
Expand All @@ -92,7 +97,7 @@ def resolve_filter_field(self, auto_schema, model, filterset_class, field_name,
elif isinstance(filter_field, (filters.NumberFilter, filters.NumericRangeFilter)):
# NumberField is underspecified by itself. try to find the
# type that makes the most sense or default to generic NUMBER
if filter_field.method:
if filter_method:
schema = self._build_filter_method_type(filterset_class, filter_field)
if schema['type'] not in ['integer', 'number']:
schema = build_basic_type(OpenApiTypes.NUMBER)
Expand Down Expand Up @@ -182,12 +187,16 @@ def resolve_filter_field(self, auto_schema, model, filterset_class, field_name,
for field_name in field_names
]

def _build_filter_method_type(self, filterset_class, filter_field):
def _get_filter_method(self, filterset_class, filter_field):
if callable(filter_field.method):
filter_method = filter_field.method
return filter_field.method
elif isinstance(filter_field.method, str):
return getattr(filterset_class, filter_field.method)
else:
filter_method = getattr(filterset_class, filter_field.method)
return None

def _build_filter_method_type(self, filterset_class, filter_field):
filter_method = self._get_filter_method(filterset_class, filter_field)
try:
filter_method_hints = get_type_hints(filter_method)
except: # noqa: E722
Expand Down
6 changes: 6 additions & 0 deletions tests/contrib/test_django_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class ProductFilter(FilterSet):
int_id = NumberFilter(method='filter_method_typed')
number_id = NumberFilter(method='filter_method_untyped', help_text='some injected help text')
number_id_ext = NumberFilter(method=external_filter_method)
email = CharFilter(method='filter_method_decorated')
# implicit filter declaration
subproduct__sub_price = NumberFilter() # reverse relation
other_sub_product__uuid = UUIDFilter() # forward relation
Expand Down Expand Up @@ -129,6 +130,11 @@ def filter_method_typed(self, queryset, name, value: int):
def filter_method_untyped(self, queryset, name, value):
return queryset.filter(id=int(value)) # pragma: no cover

# email makes no sense here. it's just to test decoration
@extend_schema_field(OpenApiTypes.EMAIL)
def filter_method_decorated(self, queryset, name, value):
return queryset.filter(id=int(value))


@extend_schema(
examples=[
Expand Down
5 changes: 5 additions & 0 deletions tests/contrib/test_django_filters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ paths:
description: Multiple values may be separated by commas.
explode: false
style: form
- in: query
name: email
schema:
type: string
format: email
- in: query
name: in_categories
schema:
Expand Down

0 comments on commit 49bb6bf

Please sign in to comment.