Понял это, с помощью Карлтона Дж. На форуме django-filters групп Google (спасибо, Карлтон).
Мое решение состояло в том, чтобы подняться на новый уровень и перехватить появившуюся схему CoreAPI.проверки AutoSchema, но до того, как она попала в автоматически сгенерированные документы.
В этой точке перехвата я переопределяю _allows_filters
, чтобы применять только к моим интересующим HTTP-глаголам.(Несмотря на то, что он префикс _
и, следовательно, предназначен как частный метод, не предназначенный для переопределения, комментарии метода явно поощряют это. Introduced in v3.7: Initially "private" (i.e. with leading underscore) to allow changes based on user experience.
Мой код ниже:
from rest_framework.schemas import AutoSchema
# see https://www.django-rest-framework.org/api-guide/schemas/#autoschema
# and https://www.django-rest-framework.org/api-guide/filtering/
class LimitedFilteringViewSchema(AutoSchema):
# Initially copied from lib/python2.7/site-packages/rest_framework/schemas/inspectors.py:352,
# then modified to restrict our filtering by query-parameters to only certain view
# actions or HTTP verbs
def _allows_filters(self, path, method):
if getattr(self.view, 'filter_backends', None) is None:
return False
if hasattr(self.view, 'action'):
return self.view.action in ["list"] # original code: ["list", "retrieve", "update", "partial_update", "destroy"]
return method.lower() in ["get"] # original code: ["get", "put", "patch", "delete"]
А потом, на моем уровне APIView:
class MyViewSchema(LimitedFilteringViewSchema):
# note to StackOverflow: this was some additional schema repair work I
# needed to do, again adding logic conditional on the HTTP verb.
# Not related to the original question posted here, but hopefully relevant
# all the same.
def get_serializer_fields(self, path, method):
fields = super(MyViewSchema, self).get_serializer_fields(path, method)
# The 'name' parameter is set in MyModelListItemSerializer as not being required.
# However, when creating an access-code-pool, it must be required -- and in DRF v3.7, there's
# no clean way of encoding this conditional logic, short of what you see here:
#
# We override the AutoSchema inspection class, so we can intercept the CoreAPI Fields it generated,
# on their way out but before they make their way into the auto-generated api docs.
#
# CoreAPI Fields are named tuples, hence the poor man's copy constructor below.
if path == u'/v1/domains/{domain_name}/access-code-pools' and method == 'POST':
# find the index of our 'name' field in our fields list
i = next((i for i, f in enumerate(fields) if (lambda f: f.name == 'name')(f)), -1)
if i >= 0:
name_field = fields[i]
fields[i] = Field(name=name_field.name, location=name_field.location,
schema=name_field.schema, description=name_field.description,
type=name_field.type, example=name_field.example,
required=True) # all this inspection, just to set this here boolean.
return fields
class MyNameFilter(FilterSet):
name = CharFilter(field_name='name', help_text='Filter returned access code pools by name')
class Meta:
model = MyModel
fields = ('name',)
class MyAPIView(...)
schema = MyViewSchema()
filter_backends = (DjangoFilterBackend,)
filter_class = MyNameFilter