带有 GenericViewSet 的 Django rest-framework:根据来自 url 的查询参数过滤结果
Django rest-framework with GenericViewSet: Filter results base on query parameters from the url
我的应用程序使用带有 ListModelMixin 的 GenericViewSet。我使用 filter_backends
和 filter_class
来过滤结果。 (请参阅下面的屏幕截图 'list':serializers.BookingListSerializer)
我正在编写以下简报:
假设我有一个预先过滤(使用 filter_backends
)然后在 UI 上显示给用户的动物列表。
用户可以根据 UI 中的某些搜索条件(比如名称、类型、颜色)进一步过滤结果。这些过滤由 filter_class
.
处理
在 UI 的单独标签中,它只显示 Dogs 类型的动物,而不是整个动物集合。并且可以再次根据名称和颜色进一步过滤。
我必须创建 2 个单独的端点来向用户显示这两种结果(以便更好地控制结果...拧干!)。但我不知道如何在 Django 中创建它们,因为 animals
和 dogs
使用相同的 Django 模态,过滤器后端和过滤器 class 仅应用于实际模态,即.在动物名单上。
我需要简单的 def list1(request)
和 def list2(request)
,我可以在其中根据请求参数和我的过滤器后端过滤 query_set
和过滤器 classes.
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',
]
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()
方法内部执行,return 一个 QuerySet
应用过滤器后。
2. 您可以使用 self.get_queryset()
方法代替 myqueryset = MyModel.objects.all()
staement,即 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>
我不太明白这个问题,但我认为您应该对自定义操作执行与 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)
我的应用程序使用带有 ListModelMixin 的 GenericViewSet。我使用 filter_backends
和 filter_class
来过滤结果。 (请参阅下面的屏幕截图 'list':serializers.BookingListSerializer)
我正在编写以下简报:
假设我有一个预先过滤(使用 filter_backends
)然后在 UI 上显示给用户的动物列表。
用户可以根据 UI 中的某些搜索条件(比如名称、类型、颜色)进一步过滤结果。这些过滤由
filter_class
. 处理
在 UI 的单独标签中,它只显示 Dogs 类型的动物,而不是整个动物集合。并且可以再次根据名称和颜色进一步过滤。
我必须创建 2 个单独的端点来向用户显示这两种结果(以便更好地控制结果...拧干!)。但我不知道如何在 Django 中创建它们,因为 animals
和 dogs
使用相同的 Django 模态,过滤器后端和过滤器 class 仅应用于实际模态,即.在动物名单上。
我需要简单的 def list1(request)
和 def list2(request)
,我可以在其中根据请求参数和我的过滤器后端过滤 query_set
和过滤器 classes.
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',
]
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()
方法内部执行,return 一个 QuerySet
应用过滤器后。
2. 您可以使用 self.get_queryset()
方法代替 myqueryset = MyModel.objects.all()
staement,即 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>
我不太明白这个问题,但我认为您应该对自定义操作执行与 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)