Фильтры Django с представлениями на основе классов: динамически применять Django FilterSet - PullRequest
0 голосов
/ 18 октября 2018

Я использую django-filters для фильтрации данных на основе классов.Я использую 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()

Однако я не могу понять, как написать то утверждение, которое вызывает FilterTwo с the query dictionary (получено в теле вызова POST) и оригинальным queryset.

Во-вторых, У меня есть поле date в моём модале, и я хочу выполнить в этом поле больший_ меньший и меньший.Мне интересно, правильно ли написана моя логика фильтра.

Ответы [ 2 ]

0 голосов
/ 20 октября 2018

Наконец, после тщательного изучения django FilterBackends и django_filters FilterSets, я понял концепцию и использование обоих.Хотя, я решил проблему, она определенно может быть улучшена дальше.(Я опубликую это как отдельный вопрос по SO).

Это решение: вместо определения нового пользовательского 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.

0 голосов
/ 18 октября 2018

Вы можете вызвать класс фильтра. Filter_queryset метод

 queryset = self.get_queryset()
 queryset = self.filter_queryset(queryset)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...