简体   繁体   English

如何将自定义过滤字段添加到 Django Rest Framework APIListView

[英]How to add custom filtering fields to an Django Rest Framework APIListView

I'm using django rest framework and I have a ListAPIView that I would like to implement a complex filtering functionality that I'm using Q filters.我正在使用 django rest 框架,我有一个 ListAPIView,我想实现一个复杂的过滤功能,我正在使用 Q 过滤器。 My question is how do I generate custom fields for this filtering?我的问题是如何为此过滤生成自定义字段?

url: path('', EventListView.as_view(), name='event_list_view'), url: path('', EventListView.as_view(), name='event_list_view'),

serializer:序列化器:

class EventSerializer(serializers.ModelSerializer):
    class Meta:
        model = Event
        exclude = ('user', )

View:看法:

class EventListView(ListAPIView):
    authentication_classes = ()
    permission_classes = ()
    serializer_class = EventSerializer

filter function:过滤器 function:

def filter_events(
        user: User, sort_by: int = DISTANCE, privacy_levels: [int] = [EVERYONE], categories: [Category] = None,
        start_date: datetime.datetime = DEFAULT_START_DATE, end_date: datetime.datetime = DEFAULT_END_DATE,
        distance: int = DEFAULT_DISTANCE, current_location: Point = None, unit: int = None,
        clubs: [UniversityClub] = None, universities: [University] = None,
        greeks: [GreekOrganization] = None, groups: [UserGroup] = None, users_p: [User] = None
                  ):
    ....

    if EVERYONE in privacy_levels:
        everyone_q = Q(privacy_level=EVERYONE, university__in=user_universities, start_time__range=[start_date, end_date])
    else:
        everyone_q = Q()
    if STUDENTS in privacy_levels:
        student_q = Q(privacy_level=STUDENTS, start_time__range=[start_date, end_date])
    else:
        student_q = Q()
    ...

I would recommend you to have a look atdjango-filter and DjangoFilterBackend for DRF :我建议你看看 DRF 的django-filterDjangoFilterBackend

The django-filter library includes a DjangoFilterBackend class which supports highly customizable field filtering for REST framework. django-filter 库包括一个 DjangoFilterBackend class,它支持 REST 框架的高度可定制的字段过滤。

django-filter is very powerful and allows you to define reusable and combinable filters. django-filter非常强大,允许您定义可重用和可组合的过滤器。 Example:例子:

class EventFilter(django_filters.FilterSet):
    privacy_level = django_filters.ChoiceFilter(choices=PRIVACY_LEVELS)

    class Meta:
        model = Event

You can also provide your own filtering method if you want to implement custom filtering logic:如果要实现自定义过滤逻辑,也可以提供自己的过滤方法

class EventFilter(django_filters.FilterSet):
    privacy_level = django_filters.ChoiceFilter(choices=PRIVACY_LEVELS, method='filter_privacy_level')

    def filter_privacy_level(self, queryset, name, value):
        # Filter upon given privacy level
        queryset = queryset.filter(privacy_level=value)

        # Apply your custom logic given the privacy_level
        if value == UNIVERSITY:
            queryset = queryset.filter(another_field='foo')

        return queryset            

    class Meta:
        model = Event

The beauty behind this is that each Filter will return a queryset ;这背后的美妙之处在于每个Filter都会返回一个queryset which allows django-filter to chain them and thus to allow any combinations of filters.这允许django-filter链接它们,从而允许过滤器的任何组合。

However, in your specific case, you'll need the current user in your filter method.但是,在您的特定情况下,您需要过滤方法中的当前用户。 We can also do that by customizing the DjangoFilterBackend :我们也可以通过自定义DjangoFilterBackend来做到这一点:

class UserFilterBackend(filters.DjangoFilterBackend):
    def get_filterset_kwargs(self, request, queryset, view):
        # Get the default constructor kwargs
        kwargs = super().get_filterset_kwargs(request, queryset, view)

        # Add the current user in it
        kwargs['user'] = request.user

        return kwargs


class EventFilter(django_filters.FilterSet):
    privacy_level = django_filters.ChoiceFilter(choices=PRIVACY_LEVELS, method='filter_privacy_level')

    # Overload the default constructor to retrieve the user in kwargs
    def __init__(self, *args, user=None, **kwargs):
        super().__init__(*args, **kwargs)
        # Keep the user in the filter instance
        self.user = user

    def filter_privacy_level(self, queryset, name, value):
        # Filter upon given privacy level
        queryset = queryset.filter(privacy_level=value)

        # Apply your custom logic given the privacy_level
        if value == UNIVERSITY:
            # Great, we can retrieve the user universities!
            queryset = queryset.filter(university__in=self.user.universities)

        return queryset            

    class Meta:
        model = Event

Of course, you should now use your UserFilterBackend instead of filters.DjangoFilterBackend in the filter_backends of your viewset.当然,您现在应该在视图集的filter_backends中使用您的UserFilterBackend而不是filters.DjangoFilterBackend

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM