I want to add _filter_ like parameters to my APIView methods, they should appear in the schema and validate the incoming data, serializers are a perfect fit for this.
In this repo:
https://github.com/domingues/django-rest-framework/blob/0997748e4fbd0f330cfcac580b446d849e50f80e/rest_framework/schemas/openapi.py#L542-L586
we have a class APIViewSchema(AutoSchema) that provides (at the moment) two decorators: @APIViewSchema.serializer(serializer: serializers.Serializer) and @APIViewSchema.query_parameters(parameters: serializers.Serializer)
and use them like this:
class MySerializer(serializers.Serializer):
count = serializers.IntegerField(help_text='Number of results.')
class MyView(views.APIView):
schema = APIViewSchema()
class Parameters(serializers.Serializer):
q = serializers.CharField(required=True, min_length=3, help_text='Search by.')
page_size = serializers.IntegerField(required=False, max_value=100, help_text='Number of results to return.')
ordering = serializers.ChoiceField(choices=['title', 'rank'], default='rank',
help_text='Order by.')
@APIViewSchema.serializer(MySerializer())
@APIViewSchema.query_parameters(Parameters())
def get(self, request):
parameters = self.Parameters(data=request.query_params)
parameters.is_valid(raise_exception=True)
data = ...
return Response(MySerializer(data).data)

I think that would be really useful if we have something like this.
I think you could implement this using either the existing DRF filters鈥攊n your example, q would be provided by a SearchFilter and ordering by an OrderingFilter, and page_size from a pagination class鈥攐r writing your own. Filters can override the get_schema_operation_parameters to provide OpenAPI query parameters. You may have a custom filter inheriting from rest_framework.filters.BaseFilterBackend that uses a serializer on an APIView to add query parameters and possibly handle them.
In this idea, I don't see the benefit of the decorator and Parameters class over just subclassing AutoSchema, and overriding the appropriate method there. (It just seems to be introducing another way of doing the same thing.)
_Possibly_, like maybe, we could introduce an __init__ kwarg to AutoSchema so you could do something like:
class MyView(APIView):
schema = AutoSchema(
extra_parameters=[...]
)
But I'm not yet convinced of the need for that. (The subclass is hardly a lot of code.)
Using a serializer means you also get validation on query parameters, which is always useful to reduce code duplication for example; why do a if len(xxx) < 20: raise ValidationError(...) when you could just have a CharField(max_length=20).
I think having more documentation and public methods on the AutoSchema would make subclassing easier and help point folks to how they can customize their schemas.
I think having more documentation and public methods on the AutoSchema would make subclassing easier and help point folks to how they can customize their schemas.
Yes, absolutely. I want to move 鈮坅ll the methods to public (i.e. no _) and document them for the next release. Easy PR land if you're looking. 馃榾
_Possibly_, like maybe, we could introduce an
__init__kwarg toAutoSchemaso you could do something like:class MyView(APIView): schema = AutoSchema( extra_parameters=[...] )
The problem of that approach is that you will have the same parameters in all methods, which may not be desirable. The same for the serializer.
Using a serializer means you also get validation on query parameters, which is always useful to reduce code duplication for example; why do a
if len(xxx) < 20: raise ValidationError(...)when you could just have aCharField(max_length=20).
Exactly.
I think having more documentation and public methods on the AutoSchema would make subclassing easier and help point folks to how they can customize their schemas.
Yes, absolutely. I want to move 鈮坅ll the methods to public (i.e. no
_) and document them for the next release. Easy PR land if you're looking. 馃榾
Maybe that's the way to go.
@Lucidiot is right here: filter parameters come from filter backends (be they django-filter, the DRF shipped ones, or your own.)
You can implement a filter backend to use serializers, to give you validation etc (which is exactly what django-filters does, except with FilterSets and forms under the hood). The relevant AutoSchema method is _get_filter_parameters(), which we'll deprecate and promote to public for 3.12.
@carlfarrington The filter can be used to generate query parameters but it's not the aim for generating query parameters.
The default behavior of REST framework's generic list views is to return the entire queryset for a model manager. Often you will want your API to restrict the items that are returned by the queryset.
The simplest way to filter the queryset of any view that subclasses GenericAPIView is to override the .get_queryset() method.
Overriding this method allows you to customize the queryset returned by the view in a number of different ways.
class BaseFilterBackend:
"""
A base class from which all filter backend classes should inherit.
"""
def filter_queryset(self, request, queryset, view):
"""
Return a filtered queryset.
"""
raise NotImplementedError(".filter_queryset() must be overridden.")
def get_schema_fields(self, view):
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
return []
def get_schema_operation_parameters(self, view):
return []
Most helpful comment
Yes, absolutely. I want to move 鈮坅ll the methods to public (i.e. no
_) and document them for the next release. Easy PR land if you're looking. 馃榾