django_filters 仅需要应用于列表操作

Yur*_*aha 6 django-filters django-rest-framework

我有一个简单的过滤器:

class ContentFilter(filters.FilterSet):
    app = filters.UUIDFilter(required=True)
    ...
Run Code Online (Sandbox Code Playgroud)

我在简单的 ModelViewSet 上使用了它

class ContentView(ModelViewSet):
    """View for Content"""
    serializer_class = ContentSerializer
    filterset_class = ContentFilter
Run Code Online (Sandbox Code Playgroud)

但是当我尝试 get /contents/:id/ 时出现错误:

{
  "app": [
    "This field is required."
  ]
}
Run Code Online (Sandbox Code Playgroud)

那么,我的问题。我怎样才能required=True只设置我的list动作?

我的解决方案是:

 def filter_queryset(self, queryset):
        """We need display filter only on list request, otherwise we get error on /id/ requests about app is required"""
        if self.action != 'list':
            self.filterset_class = None
        return super().filter_queryset(queryset)
Run Code Online (Sandbox Code Playgroud)

什么是更好的解决方案,也许我可以在过滤器类中配置它?

Daw*_*ski 1

我知道这是一个老问题,但也许有人想以不同的方式来做。我想出了重写filterset_class作为属性,并根据操作选择是否要使用filterset_class。我在代码中这样做是为了单一职责(filter_queryset 应该过滤,而不是选择一个filterset_class)。

我的代码:

from typing import Type
from django_filters import rest_framework as filters
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework.viewsets import GenericViewSet
from .filters import FooFilter


class FooViewSet(RetrieveModelMixin, ListModelMixin, GenericViewSet):
    filter_backends = (filters.DjangoFilterBackend,)

    @property
    def filterset_class(self) -> Type[filters.FilterSet] | None:
        if self.action == 'list':
            return FooFilter
Run Code Online (Sandbox Code Playgroud)