Django rest-framework с GenericViewSet: фильтр результатов на основе параметров запроса из URL - PullRequest
0 голосов
/ 29 августа 2018

Мое приложение использует GenericViewSet с ListModelMixin. Я использовал filter_backends и filter_class, чтобы отфильтровать результаты. (см. «список»: serializers.BistingListSerializer на снимке экрана ниже)

enter image description here

Я работаю над следующим заданием:

Допустим, у меня есть список животных, которые предварительно отфильтрованы (с использованием filter_backends), а затем показаны пользователю в пользовательском интерфейсе.

  1. Пользователи могут дополнительно фильтровать результаты на основе некоторых критериев поиска из пользовательского интерфейса (скажем, имя, тип, цвет). Эти фильтрации обрабатываются filter_class.

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

Я должен создать 2 отдельные конечные точки, чтобы показывать пользователю результаты обоих видов (чтобы иметь больше контроля над результатами ... ты винт СУХОЙ!). Но я не могу понять, как создать их в Django, так как animals и dogs используют один и тот же модал django, а бэкэнд-фильтр и класс фильтра применяются только к реальному модальному, т.е. в списке животных.

Мне нужны простые def list1(request) и def list2(request), где я могу отфильтровать query_set на основе параметров запроса, а также моих внутренних фильтров и классов фильтров.

api.py

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

serializer_classes = {
    'create': serializers.BookingCreateUpdateSerializer,
    'update': serializers.BookingCreateUpdateSerializer,
    'duplicate': serializers.BookingCreateUpdateSerializer,
    'list': serializers.BookingListSerializer,
    'list_drafts': serializers.BookingListSerializer,
    'create_draft': serializers.BookingCreateUpdateSerializer,
    'submit_draft': serializers.BookingCreateUpdateSerializer,
}

def create(self, request, *args, **kwargs):
    serializer = self.get_serializer(data=request.data)
    serializer.is_valid(raise_exception=True)

    booking = services.create_booking(serializer.validated_data)
    data = serializers.BookingDetailSerializer(booking, context={'request': request}).data

    return response.Created(data)

def update(self, request, *args, **kwargs):
    booking = self.get_object()

    partial = kwargs.pop('partial', False)
    serializer = self.get_serializer(booking, data=request.data, partial=partial)
    serializer.is_valid(raise_exception=True)

    booking = services.update_booking(booking, serializer.validated_data)

    async('shootsta.bookings.tasks.booking_update_google_calendar_event', booking.pk)

    data = serializers.BookingDetailSerializer(booking, context={'request': request}).data

    return response.Ok(data)

@detail_route(methods=['POST'], url_path='duplicate')
def duplicate(self, request, *args, **kwargs):
    booking = self.get_object()

    new_booking = services.duplicate_booking(booking)

    data = serializers.BookingDetailSerializer(new_booking, context={'request': request}).data

    return response.Created(data)

@list_route(methods=['GET'], url_path='list-drafts')
def list_drafts(self, request, *args, **kwargs):
    # Code goes here! Here i'll get some params from url like state and title and then return filtered the results.
    pass

@list_route(methods=['POST'], url_path='create-draft')
def create_draft(self, request, *args, **kwargs):
    serializer = self.get_serializer(data=request.data)
    serializer.is_valid(raise_exception=True)

    booking = services.create_booking(serializer.validated_data, constants.BookingMode.draft)
    data = serializers.BookingDetailSerializer(booking, context={'request': request}).data

    return response.Created(data)

@detail_route(methods=['POST'], url_path='submit-draft')
def submit_draft(self, request, *args, **kwargs):
    booking = self.get_object()
    booking.submit_draft(by=request.user)
    booking.save()

    data = serializers.BookingDetailSerializer(booking, context={'request': request}).data

    return response.Ok(data)

@detail_route(methods=['POST'], url_path='approve')
def approve(self, request, *args, **kwargs):
    booking = self.get_object()
    booking.approve(by=request.user)
    booking.save()

    data = serializers.BookingDetailSerializer(booking, context={'request': request}).data

    return response.Ok(data)

filters.py

# Standard Library
import operator
from functools import reduce

# Third Party
from django.db.models import Q
from django_filters import rest_framework as filters
from dry_rest_permissions.generics import DRYPermissionFiltersBase

# Project Local
from . import 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))


def filter_booking_title(queryset, name, value):
    """
    Split the filter value into separate search terms and construct a set of queries from this. The set of queries
    includes an icontains lookup for the lookup fields for each of the search terms. The set of queries is then joined
    with the OR operator.
    """
    lookups = ['title__icontains', ]

    or_queries = []

    search_terms = value.split()

    for search_term in search_terms:
        or_queries += [Q(**{lookup: search_term}) for lookup in lookups]

    return queryset.filter(reduce(operator.or_, or_queries))


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

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

Ответы [ 2 ]

0 голосов
/ 29 августа 2018

Я не совсем понял вопрос, но я думаю, что вы должны сделать то же самое с вашим настраиваемым действием, что и DRF в его общем списке. просто позвоните filter_queryset на ваш первоначальный запрос, например:

class your_view(....):
...
...
    def get_queryset2(self):
        return YourotherModel.objects.all() ### or any thing your i.e. specific fiter on your general model


    @action(methods=['GET'], detail=False)
    def list2(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset2()) ### call filter_queryset on your custom query

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
0 голосов
/ 29 августа 2018
class SampleViewset(.....):

    @list_route(methods=['GET'])
    def list_2(self, request, *args, **kwargs):
        <b>myqueryset = MyModel.objects.all() # or whatever queryset you need to serialize
        queryset = self.filter_queryset(myqueryset)</b>

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)


Ключевыми моментами, на которые следует обратить внимание, являются:

1. Процесс фильтрации выполняется в методе self.filter_queryset(), который возвращает QuerySet после применения фильтра.
2. Вы можете использовать метод self.get_queryset() вместо myqueryset = MyModel.objects.all(), который является DRF-способом для выполнения таких действий.

UPDATE-1
Если вы хотите использовать значение по умолчанию queryset, вы можете использовать метод get_queryset() как,

class SampleViewset(.....):

    @list_route(methods=['GET'])
    def list_2(self, request, *args, **kwargs):
        <b>queryset = self.filter_queryset(self.get_queryset())</b>

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

или просто

class SampleViewset(.....):

    @list_route(methods=['GET'])
    <b>def list_2(self, request, *args, **kwargs):
        return self.list(self, request, *args, **kwargs)</b>
...