mixins.py generics.py

通用视图

Django 的通用视图... 是作为常见使用模式的快捷方式开发的... 它们采用视图开发中发现的某些常见习语和模式,并对其进行抽象,以便您能够快速编写常见的数据视图,而无需重复自己。

Django 文档

基于类的视图的一个主要优点是它们允许您组合可重用行为的片段。REST 框架通过提供许多预构建视图来利用这一点,这些视图提供了常用的模式。

REST 框架提供的通用视图允许您快速构建与数据库模型紧密映射的 API 视图。

如果通用视图不适合您的 API 的需求,您可以使用常规的 APIView 类,或者重用通用视图使用的 Mixin 和基类来组合您自己的一组可重用通用视图。

示例

通常在使用通用视图时,您将覆盖视图并设置几个类属性。

from django.contrib.auth.models import User
from myapp.serializers import UserSerializer
from rest_framework import generics
from rest_framework.permissions import IsAdminUser

class UserList(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAdminUser]

对于更复杂的情况,您可能还想覆盖视图类上的各种方法。例如。

class UserList(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAdminUser]

    def list(self, request):
        # Note the use of `get_queryset()` instead of `self.queryset`
        queryset = self.get_queryset()
        serializer = UserSerializer(queryset, many=True)
        return Response(serializer.data)

对于非常简单的情况,您可能希望使用 .as_view() 方法传递任何类属性。例如,您的 URLconf 可能包含类似以下内容的条目

path('users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')

API 参考

GenericAPIView

此类扩展了 REST 框架的 APIView 类,为标准列表和详细信息视图添加了通常需要的行为。

提供的每个具体通用视图都是通过将 GenericAPIView 与一个或多个 Mixin 类相结合来构建的。

属性

基本设置:

以下属性控制基本视图行为。

  • queryset - 应用于从此视图返回对象的查询集。通常,您必须设置此属性或覆盖 get_queryset() 方法。如果您要覆盖视图方法,则务必调用 get_queryset() 而不是直接访问此属性,因为 queryset 将被评估一次,并且这些结果将被缓存以用于所有后续请求。
  • serializer_class - 应用于验证和反序列化输入以及序列化输出的序列化程序类。通常,您必须设置此属性或覆盖 get_serializer_class() 方法。
  • lookup_field - 应用于执行单个模型实例的对象查找的模型字段。默认为 'pk'。请注意,在使用超链接 API 时,如果您需要使用自定义值,您需要确保 API 视图和序列化程序类都设置了查找字段。
  • lookup_url_kwarg - 应用于对象查找的 URL 关键字参数。URL 配置应包括对应于此值的关键字参数。如果未设置,则默认为使用与 lookup_field 相同的值。

分页:

以下属性用于在与列表视图结合使用时控制分页。

  • pagination_class - 在对列表结果进行分页时应使用的分页类。默认为与 DEFAULT_PAGINATION_CLASS 设置相同的值,即 'rest_framework.pagination.PageNumberPagination'。设置 pagination_class=None 将禁用此视图上的分页。

过滤:

  • filter_backends - 应用于过滤查询集的过滤器后端类的列表。默认为与 DEFAULT_FILTER_BACKENDS 设置相同的值。

方法

基本方法:

get_queryset(self)

返回应用于列表视图的查询集,并且应作为详细视图中查找的基础。默认为返回由 queryset 属性指定的查询集。

应始终使用此方法,而不是直接访问 self.queryset,因为 self.queryset 仅被评估一次,并且这些结果被缓存以用于所有后续请求。

可以覆盖以提供动态行为,例如返回特定于发出请求用户的查询集。

例如

def get_queryset(self):
    user = self.request.user
    return user.accounts.all()

注意:如果通用视图中使用的 serializer_class 跨越 orm 关系,导致 n+1 问题,则可以使用 select_relatedprefetch_related 在此方法中优化查询集。要获得有关 n+1 问题和所述方法用例的更多信息,请参阅 django 文档 中的相关部分。


get_object(self)

返回应用于详细视图的对象实例。默认为使用 lookup_field 参数过滤基本查询集。

可以覆盖以提供更复杂的行为,例如基于多个 URL kwarg 的对象查找。

例如

def get_object(self):
    queryset = self.get_queryset()
    filter = {}
    for field in self.multiple_lookup_fields:
        filter[field] = self.kwargs[field]

    obj = get_object_or_404(queryset, **filter)
    self.check_object_permissions(self.request, obj)
    return obj

请注意,如果您的 API 不包含任何对象级别权限,则可以选择排除 self.check_object_permissions,并仅从 get_object_or_404 查找中返回对象。

filter_queryset(self, queryset)

给定一个查询集,使用正在使用的任何过滤器后端对其进行过滤,返回一个新的查询集。

例如

def filter_queryset(self, queryset):
    filter_backends = [CategoryFilter]

    if 'geo_route' in self.request.query_params:
        filter_backends = [GeoRouteFilter, CategoryFilter]
    elif 'geo_point' in self.request.query_params:
        filter_backends = [GeoPointFilter, CategoryFilter]

    for backend in list(filter_backends):
        queryset = backend().filter_queryset(self.request, queryset, view=self)

    return queryset

get_serializer_class(self)

返回应用于序列化程序的类。默认为返回 serializer_class 属性。

可以覆盖以提供动态行为,例如对读写操作使用不同的序列化程序,或对不同类型的用户提供不同的序列化程序。

例如

def get_serializer_class(self):
    if self.request.user.is_staff:
        return FullAccountSerializer
    return BasicAccountSerializer

保存和删除挂钩:

以下方法由 mixin 类提供,并提供对对象保存或删除行为的简单覆盖。

  • perform_create(self, serializer) - 在保存新对象实例时由 CreateModelMixin 调用。
  • perform_update(self, serializer) - 在保存现有对象实例时由 UpdateModelMixin 调用。
  • perform_destroy(self, instance) - 在删除对象实例时由 DestroyModelMixin 调用。

这些挂钩对于设置请求中隐含但不是请求数据一部分的属性特别有用。例如,您可以根据请求用户或基于 URL 关键字参数在对象上设置属性。

def perform_create(self, serializer):
    serializer.save(user=self.request.user)

这些覆盖点对于添加在保存对象之前或之后发生的的行为也特别有用,例如发送确认电子邮件或记录更新。

def perform_update(self, serializer):
    instance = serializer.save()
    send_email_confirmation(user=self.request.user, modified=instance)

您还可以使用这些挂钩通过引发 ValidationError() 提供额外的验证。如果您需要在数据库保存时应用一些验证逻辑,这将非常有用。例如

def perform_create(self, serializer):
    queryset = SignupRequest.objects.filter(user=self.request.user)
    if queryset.exists():
        raise ValidationError('You have already signed up')
    serializer.save(user=self.request.user)

其他方法:

通常不需要覆盖以下方法,但如果您使用 GenericAPIView 编写自定义视图,则可能需要调用它们。

  • get_serializer_context(self) - 返回一个包含应提供给序列化程序的任何额外上下文信息。默认为包括 'request''view''format' 键。
  • get_serializer(self, instance=None, data=None, many=False, partial=False) - 返回一个序列化程序实例。
  • get_paginated_response(self, data) - 返回分页样式的 Response 对象。
  • paginate_queryset(self, queryset) - 如果需要,对查询集进行分页,返回一个页面对象,如果此视图未配置分页,则返回 None
  • filter_queryset(self, queryset) - 给定一个查询集,使用正在使用的任何过滤器后端对其进行过滤,返回一个新的查询集。

Mixin

混合类提供用于提供基本视图行为的动作。请注意,混合类提供动作方法,而不是直接定义处理程序方法,例如 .get().post()。这允许更灵活的行为组合。

混合类可以从 rest_framework.mixins 导入。

ListModelMixin

提供 .list(request, *args, **kwargs) 方法,用于实现查询集的列出。

如果填充了查询集,则返回 200 OK 响应,其中包含查询集的序列化表示形式作为响应的主体。响应数据可以根据需要进行分页。

CreateModelMixin

提供 .create(request, *args, **kwargs) 方法,用于实现创建和保存新模型实例。

如果创建了一个对象,则返回 201 Created 响应,其中包含对象的序列化表示形式作为响应的主体。如果表示形式包含名为 url 的键,则响应的 Location 头将填充该值。

如果为创建对象提供的数据请求无效,则将返回 400 Bad Request 响应,其中包含错误详细信息作为响应的主体。

RetrieveModelMixin

提供 .retrieve(request, *args, **kwargs) 方法,用于实现返回响应中的现有模型实例。

如果可以检索到一个对象,则返回 200 OK 响应,其中包含对象的序列化表示形式作为响应的主体。否则,它将返回 404 Not Found

UpdateModelMixin

提供 .update(request, *args, **kwargs) 方法,用于实现更新和保存现有模型实例。

还提供 .partial_update(request, *args, **kwargs) 方法,它类似于 update 方法,除了更新的所有字段都是可选的。这允许支持 HTTP PATCH 请求。

如果更新了一个对象,则返回 200 OK 响应,其中包含对象的序列化表示形式作为响应的主体。

如果为更新对象提供的数据请求无效,则将返回 400 Bad Request 响应,其中包含错误详细信息作为响应的主体。

DestroyModelMixin

提供 .destroy(request, *args, **kwargs) 方法,用于实现现有模型实例的删除。

如果删除了一个对象,则返回 204 No Content 响应,否则将返回 404 Not Found


具体视图类

以下类是具体通用视图。如果您正在使用通用视图,除非您需要高度定制的行为,否则这通常是您将要使用的级别。

视图类可以从 rest_framework.generics 导入。

CreateAPIView

用于仅创建端点。

提供 post 方法处理程序。

扩展: GenericAPIViewCreateModelMixin

ListAPIView

用于仅读取端点以表示模型实例集合

提供 get 方法处理程序。

扩展: GenericAPIViewListModelMixin

RetrieveAPIView

用于仅读取端点以表示单个模型实例

提供 get 方法处理程序。

扩展: GenericAPIViewRetrieveModelMixin

DestroyAPIView

用于仅删除端点,用于单个模型实例

提供 delete 方法处理程序。

扩展: GenericAPIViewDestroyModelMixin

UpdateAPIView

用于仅更新端点,用于单个模型实例

提供 putpatch 方法处理程序。

扩展: GenericAPIViewUpdateModelMixin

ListCreateAPIView

用于读写端点以表示模型实例集合

提供 getpost 方法处理程序。

扩展: GenericAPIViewListModelMixinCreateModelMixin

RetrieveUpdateAPIView

用于读取或更新端点以表示单个模型实例

提供 getputpatch 方法处理程序。

扩展: GenericAPIViewRetrieveModelMixinUpdateModelMixin

RetrieveDestroyAPIView

用于读取或删除端点以表示单个模型实例

提供 getdelete 方法处理程序。

扩展: GenericAPIViewRetrieveModelMixinDestroyModelMixin

RetrieveUpdateDestroyAPIView

用于读写删除端点以表示单个模型实例

提供 getputpatchdelete 方法处理程序。

扩展: GenericAPIViewRetrieveModelMixinUpdateModelMixinDestroyModelMixin


自定义通用视图

通常您会希望使用现有的通用视图,但使用一些略微定制的行为。如果您发现自己在多个地方重复使用一些定制行为,您可能希望将行为重构到一个通用类中,然后根据需要将其应用于任何视图或视图集。

创建自定义 Mixin

例如,如果你需要根据 URL 配置中的多个字段查找对象,你可以创建一个类似以下内容的 mixin 类

class MultipleFieldLookupMixin:
    """
    Apply this mixin to any view or viewset to get multiple field filtering
    based on a `lookup_fields` attribute, instead of the default single field filtering.
    """
    def get_object(self):
        queryset = self.get_queryset()             # Get the base queryset
        queryset = self.filter_queryset(queryset)  # Apply any filter backends
        filter = {}
        for field in self.lookup_fields:
            if self.kwargs.get(field): # Ignore empty fields.
                filter[field] = self.kwargs[field]
        obj = get_object_or_404(queryset, **filter)  # Lookup the object
        self.check_object_permissions(self.request, obj)
        return obj

然后,你可以在需要应用自定义行为时,将此 mixin 应用到视图或视图集。

class RetrieveUserView(MultipleFieldLookupMixin, generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    lookup_fields = ['account', 'username']

如果你有需要使用的自定义行为,使用自定义 mixin 是一个不错的选择。

创建自定义基类

如果你在多个视图中使用 mixin,你可以进一步创建自己的基本视图集,然后可以在整个项目中使用它们。例如

class BaseRetrieveView(MultipleFieldLookupMixin,
                       generics.RetrieveAPIView):
    pass

class BaseRetrieveUpdateDestroyView(MultipleFieldLookupMixin,
                                    generics.RetrieveUpdateDestroyAPIView):
    pass

如果你有需要在整个项目的大量视图中重复的自定义行为,使用自定义基本类是一个不错的选择。


PUT 作为创建

在 3.0 版本之前,REST 框架 mixin 将 PUT 视为更新或创建操作,具体取决于对象是否存在。

允许 PUT 作为创建操作是有问题的,因为它必然会公开有关对象是否存在的信息。而且,显然允许重新创建以前删除的实例不一定比简单地返回 404 响应更好的默认行为。

在不同的情况下,“PUT 作为 404”和“PUT 作为创建”两种方式都可能是有效的,但从 3.0 版本开始,我们现在使用 404 行为作为默认行为,因为它更简单、更明显。

如果你需要通用的 PUT-as-create 行为,你可能希望将类似 AllowPUTAsCreateMixin 作为 mixin 包含到你的视图中。


第三方包

以下第三方包提供了其他通用视图实现。

Django Rest 多个模型

Django Rest Multiple Models 提供了一个通用视图(和 mixin),用于通过单个 API 请求发送多个序列化模型和/或查询集。