Как добавить пользовательские поля фильтрации в Django Rest Framework APIListView - PullRequest
1 голос
/ 21 октября 2019

Я использую django rest framework и у меня есть ListAPIView, в котором я хотел бы реализовать сложную функциональность фильтрации, используя Q-фильтры. У меня вопрос, как я могу создать настраиваемые поля для этой фильтрации?

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

сериализатор:

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

Просмотр:

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

Функция фильтра:

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()
    ...

1 Ответ

0 голосов
/ 22 октября 2019

Я бы рекомендовал вам взглянуть на django-filter и DjangoFilterBackend для DRF :

Библиотека django-filter включает в себя:Класс DjangoFilterBackend, который поддерживает настраиваемую фильтрацию полей для среды REST.

django-filter очень мощный и позволяет вам определять повторно используемые и комбинируемые фильтры. Пример:

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

    class Meta:
        model = Event

Вы также можете предоставить свой собственный метод фильтрации , если вы хотите реализовать собственную логику фильтрации:

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

Вся прелесть в том, что каждыйFilter вернет queryset;что позволяет django-filter объединять их в цепочку и, следовательно, разрешать любые комбинации фильтров.

Однако в вашем конкретном случае вам понадобится текущий пользователь в вашем методе фильтрации. Мы также можем сделать это, настроив 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

Конечно, теперь вы должны использовать UserFilterBackend вместо filters.DjangoFilterBackend в filter_backends вашегоViewset.

...