具有基于 class 的视图的 Django 过滤器:动态应用 Django FilterSet

Django-Filters with class-based views: Apply Django FilterSet dynamically

我正在使用 django-filters 在基于 Class 的视图集上过滤数据。我在基于 class 的视图上使用 filter_class,它对视图集进行初始过滤。我有一个单独的过滤器,可以按需过滤输出。

filters.py

class BookingFilterBackend(DRYPermissionFiltersBase):
    def filter_list_queryset(self, request, queryset, view):
        if request.user.is_role_admin:
            return queryset

        if request.user.is_role_client:
            return queryset.filter(Q(client=request.user.client))

        if request.user.is_role_camop:
            return queryset.filter(Q(camera_operator=request.user))

        return queryset.filter(Q(created_by=request.user))

class FilterOne(filters.FilterSet):
    title = filters.CharFilter(method=filter_booking_title)

    class Meta:
        model = models.Booking
        fields = [
            'title',
            'state',
            'client',
        ]


class FilterTwo(filters.FilterSet):
    client = filters.ModelMultipleChoiceFilter(queryset=users_models.Client.objects.all())

    state = filters.MultipleChoiceFilter(choices=constants.BookingState)

    camera_operator = filters.ModelMultipleChoiceFilter(queryset=users_models.UserManager.camop_users())

    date_start = filters.DateFilter(name='date', lookup_expr='startswith')

    date_end = filters.DateFilter(name='date', lookup_expr='endswith')

    class Meta:
        model = models.Booking
        fields = [
            'state',
            'client',
            'camera_operator',
            'date_start',
            'date_end',
        ]

api.py

class MyViewSet(
    MultipleSerializerMixin,
    mixins.CreateModelMixin,
    mixins.RetrieveModelMixin,
    mixins.UpdateModelMixin,
    mixins.ListModelMixin,
    viewsets.GenericViewSet
):
    lookup_field = 'uuid'
    queryset = models.Booking.objects.all()
    filter_backends = [filters.BookingFilterBackend, DjangoFilterBackend, ]
    filter_class = filters.FilterOne
    pagination_class = BookingViewSetPagination
    serializer_class = serializers.BookingDetailSerializer

    serializer_classes = {
        ...            
    }

    @list_route(methods=['POST'], url_path='export-bookings')
    def export_bookings(self, request, *args, **kwargs):
    queryset = self.get_queryset()

    // just some debugging code
    query_dict = request.data
    print(query_dict.get('state', []))
    print(query_dict.get('clients', []))
    print(query_dict.get('camera_operators', []))
    print(query_dict.get('from_date', ''))
    print(query_dict.get('to_date', ''))

    // Apply the filter set - FilterTwo - on my model objects -> Booking. Something like this:
    // filtered_queryset = filters.FilterTwo(queryset, query_dict) - ??

    return response.NoContent()

但是,我无法弄清楚如何编写调用 FilterTwothe query dictionary 的语句(在 POST 调用的主体中收到)和原始 queryset.

其次,我的模式中有一个 date 字段,我想在该字段上执行 greater_than 和 lesser_than。请问我的Filter逻辑写的对不对

可以调用过滤器classfilter_queryset方法

 queryset = self.get_queryset()
 queryset = self.filter_queryset(queryset)

最后,在深入研究了 django FilterBackends 和 django_filters 的 FilterSets 之后,我理解了两者的概念和用法。虽然,我已经解决了这个问题,但它肯定可以进一步改进。 (我会 post 它作为一个单独的问题)。

这是解决方案: 我没有定义新的自定义 FilterSet,而是必须创建一个新的 FilterBackend 并覆盖 filter_queryset 方法。

filters.py

from django.db.models import Q
from django_filters import rest_framework as filters
from dry_rest_permissions.generics import DRYPermissionFiltersBase
from rest_framework import filters as generic_filters

# Project Local
from . import models, constants
from shootsta.users import models as users_models


class BookingFilterBackend(DRYPermissionFiltersBase):
    def filter_list_queryset(self, request, queryset, view):
        if request.user.is_role_admin:
            return queryset

        if request.user.is_role_client:
            return queryset.filter(Q(client=request.user.client))

        if request.user.is_role_camop:
            return queryset.filter(Q(camera_operator=request.user))

        return queryset.filter(Q(created_by=request.user))

# This is my new filter backend (I know the filtration logic looks very basic, I've come from a java background so spare me the rant.)
class MyNewFilterBackend(generic_filters.BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        predicate = request.data

        if all(k in predicate for k in ('from_date', 'to_date')):
            queryset = queryset.filter(date__range=(predicate['from_date'], predicate['to_date']))

        if 'from_date' in predicate and 'to_date' not in predicate:
            queryset = queryset.filter(date__gte=predicate['from_date'])

        if 'to_date' in predicate and 'from_date' not in predicate:
            queryset = queryset.filter(date__lte=predicate['to_date'])

        if 'state' in predicate:
            queryset = queryset.filter(state__in=predicate['state'])

        if 'clients' in predicate:
            queryset = queryset.filter(client__in=predicate['clients'])

        if 'camera_operators' in predicate:
            queryset = queryset.filter(camera_operator__uuid__in=predicate['camera_operators'])

        if 'recipients' in predicate:
            queryset = queryset.filter(users__uuid__in=predicate['recipients'])

        return queryset

这是我的 api.py,我刚刚将这个新的过滤器后端添加到列表中。

api.py

class MyViewSet(
    MultipleSerializerMixin,
    mixins.CreateModelMixin,
    mixins.RetrieveModelMixin,
    mixins.UpdateModelMixin,
    mixins.ListModelMixin,
    viewsets.GenericViewSet
):

...

filter_backends = [filters.BookingFilterBackend, filters. MyNewFilterBackend, DjangoFilterBackend, ]
...


# This is the api which receives filteration form data from frontend.
@list_route(methods=['POST'], url_path='export-bookings')
def export_bookings(self, request, *args, **kwargs):
    queryset = self.get_queryset()

    # filter results!
    filtered_queryset = self.filter_queryset(queryset)

    tasks.generate_export_data_and_notify_accounts(filtered_queryset)

    return response.NoContent()

我仍然想知道是否可以使用 FilterSets 实现此目的。