Django Rest Framework的基础组件之视图:通用视图API Reference与视图集

基于函数的视图、基于类的视图的使用,见Django Rest Framework:进一步理解序列化之基于函数的视图Django Rest Framework:进一步理解序列化之基于类的视图
这里有关于一、三的简单使用示例:用Django Rest Framework实现豆瓣API

一,通用视图API Reference

1,GenericAPIView

该类扩展了REST framework 的 APIView 类,为标准列表和详细视图添加了通常所需的行为。
每个具体的通用视图都是通过将 GenericAPIView 类和一个或多个 minxin 类相互结合来构建的。
1,属性:
基本设置:

  • queryset —— 用于从该视图返回对象的查询集。通常,您必须设置此属性,或重写 get_queryset() 方法。如果您正在重写视图方法,那么重要的是调用 get_queryset(),而不是直接访问此属性,因为 queryset 将被评估一次,并且这些结果将被缓存用于所有后续请求。
  • serializer_class —— 用于验证和反序列化输入以及序列化输出的序列化类。通常,您必须设置此属性,或重写 get_serializer_class() 方法
  • lookup_field —— 应该用于执行单个模型实例的对象查找的模型字段。默认为 ‘pk’。注意,在使用超链接 API 时,如果需要使用自定义值,则需要确保 API 视图和序列化器类都设置查找字段。
  • lookup_url_kwarg —— 用于对象查找的 URL 关键字参数。URL conf 应该包含与此值相对应的关键字参数。如果取消设置默认值,使用与 lookup_field 相同的值。
    分页:
    与列表视图一起使用时,以下属性用于控制分页。
  • pagination_class —— 在对列表结果进行分页时应该使用的分页类。默认为与 DEFAULT_PAGINATION_CLASS 设置相同的值,即 ‘rest_framework.pagination.PageNumberPagination’,需要在settings.py中进行如下配置。设置 pagination_class = None 将禁用此视图的分页。
# 分页设置
REST_FRAMEWORK = {
    
    
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    # 每页显示多少条数据
    'PAGE_SIZE': 2
}

过滤

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

2,方法
基本方法:

  • get_queryset(self)
    返回用于列表视图的查询集,并用作详细视图中查询的基础。默认返回 queryset 属性指定的查询集。
    应该始终使用此方法,而不是直接访问 self.queryset,因为 self.queryset 只被评估一次,并且这些结果将被缓存用于所有后续请求。
    可以重写来提供动态行为,例如返回查询集,这是针对请求的用户所特有的。
def get_queryset(self):
    user = self.request.user
    return user.accounts.all()
  • get_object(self)
    返回用于详细视图的对象实例。默认使用 lookup_field 参数来过滤基本查询集。
    可以重写以提供更复杂的行为,例如基于多个 URL kwarg 的对象查找。
    如果 API 不包含任何对象级权限,您可以选择性地排除 self.check_object_permissions,并简单地从 get_object_or_404 查找中返回对象。
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
  • 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。
pg = PageNumberPagination()
p = pg.paginate_queryset(queryset=q, request=request)
# 将分页后的数据传递MySerializer,生成JSON数据对象
serializer = MySerializer(instance=p, many=True)
  • filter_queryset(self, queryset) —— 给定一个查询集,使用任何后端过滤器进行过滤,返回一个新的查询集。

2,Mixins类

mixin 类提供用于提供基本视图行为的操作。请注意,mixin 类提供了操作方法,而不是直接定义处理方法,如 .get() 和 .post()。这允许更灵活的行为组合。
mixin 类可以从 rest_framework.mixins 导入。
1,ListModelMixin
提供 .list(request,*args,**kwargs) 方法,它实现列出查询集。
如果填充了查询集,则返回 200 OK 响应,并将查询集的序列化表示作为响应的主体。响应数据可以选择分页。
2,CreateModelMixin
提供 .create(request,* args,** kwargs) 方法,用于实现创建和保存新模型实例。
如果对象被创建,则返回一个 201 Created 响应,并将该对象的序列化表示形式作为响应的主体。如果该表示包含名为 url 的键,则响应的 Location header 将填充该值。
如果为创建对象而提供的请求数据无效,则将返回 400 Bad Request 响应,并将错误详细信息作为响应的主体。

**
就像之前用到的那样,将HTTP的请求处理绑定到具体方法就可以了。
**
class SnippetList(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

3,RetrieveModelMixin
提供 .retrieve(request,* args,** kwargs) 方法,用于实现在响应中返回现有模型实例。
如果可以检索对象,则返回一个 200 OK 响应,并将对象的序列化表示作为响应的主体。否则它将返回 404 Not Found。

4,UpdateModelMixin
提供 .update(request,* args,** kwargs) 方法,用于实现更新和保存现有模型实例。
还提供了 .partial_update(request,* args,** kwargs) 方法,该方法与 update 方法类似,不同之处在于更新的所有字段都是可选的。这允许支持 HTTP PATCH 请求。
如果对象被更新,则返回 200 OK 响应,并将对象的序列化表示作为响应的主体。
如果为更新对象而提供的请求数据无效,则将返回 400 Bad Request 响应,并将错误详细信息作为响应的主体。
5,DestroyModelMixin
提供 .destroy(request,* args,** kwargs) 方法,用于实现对现有模型实例的删除。
如果对象被删除,则返回 204 No Content,否则它将返回 404 Not Found。

**
同样的使用方法
**
class SnippetDetail(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

3,具体视图类 (Concrete View Classes)

以下类是具体的通用视图。如果你使用通用视图,这通常是你要工作的级别,除非你需要大量定制的行为。
可以从 rest_framework.generics 导入具体视图类。

  • CreateAPIView 用于仅创建端点。
    提供 post 方法处理程序。
    扩展:GenericAPIView,CreateModelMixin
  • ListAPIView 用只读端点表示模型实例的集合。
    提供 get 方法处理程序。
    扩展:GenericAPIView,ListModelMixin
  • RetrieveAPIView 用只读端点表示单个模型实例。
    提供 get 方法处理程序。
    扩展:GenericAPIView, RetrieveModelMixin
  • DestroyAPIView 用于仅删除单个模型实例的端点。
    提供 delete 方法处理程序。
    扩展:GenericAPIView, DestroyModelMixin
  • UpdateAPIView 用于只更新单个模型实例的端点。
    提供 put 和 patch 方法处理程序。
    扩展:GenericAPIView, UpdateModelMixin
  • ListCreateAPIView 用读写端点来表示模型实例的集合。
    提供 get 和 post 方法处理程序。
    扩展:GenericAPIView, ListModelMixin, CreateModelMixin
  • RetrieveUpdateAPIView 用于读取或更新端点以表示单个模型实例。
    提供 get,put 和 patch 方法处理程序。
    扩展:GenericAPIView, RetrieveModelMixin, UpdateModelMixin
  • RetrieveDestroyAPIView 用于读取或删除端点以表示单个模型实例。
    提供 get 和 delete 方法处理程序。
    扩展:GenericAPIView, RetrieveModelMixin, DestroyModelMixin
  • RetrieveUpdateDestroyAPIView 用于读写删除端点以表示单个模型实例。
    提供 get ,put,patch 和 delete 方法处理程序。
    扩展:GenericAPIView, RetrieveModelMixin, UpdateModelMixin,
    DestroyModelMixin

二,自定义通用视图

1,创建自定义 mixins

例如,如果您需要根据 URL conf 中的多个字段查找对象,则可以创建如下所示的 mixin 类:

class MultipleFieldLookupMixin(object):
    """
    将此 mixin 应用于任何视图或视图集,以基于`lookup_fields`属性获得多个字段过滤,而不是默认的单字段过滤。
    """
    def get_object(self):
        queryset = self.get_queryset()             # 获取基本查询集
        queryset = self.filter_queryset(queryset)  # 应用过滤器
        filter = {
    
    }
        for field in self.lookup_fields:
            if self.kwargs[field]: # 忽略空字段
                filter[field] = self.kwargs[field]
        obj = get_object_or_404(queryset, **filter)  # 查找对象
        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')

2,创建自定义基类

如果要在多个视图中使用 mixin,则可以更进一步,创建自己的一组基本视图,然后可以在整个项目中使用。例如:

class BaseRetrieveView(MultipleFieldLookupMixin,
                       generics.RetrieveAPIView):
    pass

class BaseRetrieveUpdateDestroyView(MultipleFieldLookupMixin,
								generics.RetrieveUpdateDestroyAPIView):
    pass

三,视图集

Django REST framework 允许您将一组相关视图的逻辑组合在一个单独的类中,称为 ViewSet
ViewSet 类只是一种基于类的视图类型,它不提供任何方法处理程序,如 .get() 或 .post(),而是提供诸如 .list().create() 之类的操作。

1,一个例子

定义一个简单的视图集,可用于列出或检索系统中的所有用户:

from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from myapps.serializers import UserSerializer
from rest_framework import viewsets
from rest_framework.response import Response

class UserViewSet(viewsets.ViewSet):
    """
    A simple ViewSet for listing or retrieving users.
    """
    def list(self, request):
        queryset = User.objects.all()
        serializer = UserSerializer(queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        queryset = User.objects.all()
        user = get_object_or_404(queryset, pk=pk)
        serializer = UserSerializer(user)
        return Response(serializer.data)

如果需要,我们可以将此视图集绑定到两个单独的视图中,如下所示:

user_list = UserViewSet.as_view({
    
    'get': 'list'})
user_detail = UserViewSet.as_view({
    
    'get': 'retrieve'})

通常我们不会这样做,而是用一个路由器注册视图集,并允许自动生成 urlconf:

from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'users', UserViewSet, base_name='user')
urlpatterns = router.urls

通常希望使用提供默认行为集合的现有基类,而不是编写自己的视图集:

class UserViewSet(viewsets.ModelViewSet):
    """
    用于查看和编辑用户实例的视图集。
    """
    serializer_class = UserSerializer
    queryset = User.objects.all()

通过上面的例子,可以知道,与使用 View 类相比,使用ViewSet有两个主要优点:

  • 重复的逻辑可以组合成一个类。在上面的示例中,我们只需要指定一次 queryset,它将在多个视图中使用。
  • 通过使用 routers,我们不再需要处理自己的 URL 连接。

2,视图集操作

REST framework 中包含的默认路由器将为一套标准的增删改查操作提供路由,如下所示:

class UserViewSet(viewsets.ViewSet):
    """
    示例空视图集演示由路由器类处理的标准操作。

    如果使用的是格式后缀,请确保每个操作还包括 `format=None` 关键字参数。
    """

    def list(self, request):
        pass

    def create(self, request):
        pass

    def retrieve(self, request, pk=None):
        pass

    def update(self, request, pk=None):
        pass

    def partial_update(self, request, pk=None):
        pass

    def destroy(self, request, pk=None):
        pass

3,反思视图集操作

在调度期间,ViewSet 上提供了以下属性。

  • basename —— 用于创建的 URL 的基础名称。
  • action —— 当前操作的名称 (例如,list,create)。
  • detail —— 布尔值指示当前动作是否配置为列表或详细视图。
  • suffix —— 视图集类型的显示后缀 - 反射详细属性。

可以检查这些属性以根据当前操作调整行为。例如,可以限制除了与 list 类似的所有权限:

    """
    实例化并返回该视图所需的权限列表。
    """
    if self.action == 'list':
        permission_classes = [IsAuthenticated]
    else:
        permission_classes = [IsAdmin]
    return [permission() for permission in permission_classes]

4,标记路由的额外操作

如果有可路由的特殊方法,可以用 @action 装饰器来标记它们。与常规操作一样,额外的操作可以用于对象列表或单个实例。为标明这一点,需要设置 detail 参数为 True 或 False。路由器将相应地配置其 URL 模式。例如,DefaultRouter 将在其 URL 模式中配置包含 pk 的详细操作。
更完整的额外操作例子:

from django.contrib.auth.models import User
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from myapp.serializers import UserSerializer, PasswordSerializer

class UserViewSet(viewsets.ModelViewSet):
    """
    提供标准操作的视图集
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @action(methods=['post'], detail=True)
    def set_password(self, request, pk=None):
        user = self.get_object()
        serializer = PasswordSerializer(data=request.data)
        if serializer.is_valid():
            user.set_password(serializer.data['password'])
            user.save()
            return Response({
    
    'status': 'password set'})
        else:
            return Response(serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False)
    def recent_users(self, request):
        recent_users = User.objects.all().order('-last_login')
        page = self.paginate_queryset(recent_users)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        serializer = self.get_serializer(recent_users, many=True)
        return Response(serializer.data)

另外,装饰器可以为路由视图设置额外的参数:

	@action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])
    def set_password(self, request, pk=None):
       ...

这些装饰器将默认路由 GET 请求,但也可以通过设置 methods 参数来接受其他 HTTP 方法:

	@action(methods=['post', 'delete'], detail=True)
    def unset_password(self, request, pk=None):
       ...

两个新操作将在url ^users/{pk}/set_password/$ 和 ^users/{pk}/unset_password/$ 上可用。
要查看所有额外操作,请调用 .get_extra_actions() 方法。

5,反转操作 urls

如果你需要获取操作的 URL,请使用 .reverse_action() 方法。这是 Reverse() 的便捷封装,它自动传递视图的 request 对象,并在 url_name 前加上 .basename 属性
请注意,basename 是路由器在 ViewSet 注册期间提供的。如果不使用路由器,则必须向 .as_view() 方法提供 basename 参数:

>>> view.reverse_action('set-password', args=['1'])
'http://localhost:8000/api/users/1/set_password'

或者,您可以使用 @action 装饰器设置的 url_name 属性:

>>> view.reverse_action(view.set_password.url_name, args=['1'])
'http://localhost:8000/api/users/1/set_password'
  • .reverse_action() 的 url_name 参数应该与 @action 装饰器匹配相同的参数。此外,此方法可用于反转默认操作,例如 list 和 create。

四,视图集API Reference

1,ViewSet

ViewSet 类继承自 APIView。您可以使用任何标准属性 (例如 permission_classes,authentication_classes) 来控制视图集上的 API 策略。
ViewSet 类不提供任何操作的实现。为了使用 ViewSet 类,您将重写该类并显式地定义操作的实现。

2,GenericViewSet

GenericViewSet 类继承自 GenericAPIView ,并提供默认的 get_object,get_queryset 方法和其他通用视图基本行为,但默认情况下不包含任何操作。
为了使用 GenericViewSet 类,您将重写该类,或混合所需的 mixin 类,或者显式定义操作实现。

3,ModelViewSet

ModelViewSet 类继承自 GenericAPIView,并通过混合各种 mixin 类的行为来包含各种操作的实现。
ModelViewSet 类提供的操作有:.list(),.retrieve(),.create(),.update(),.partial_update() 和 .destroy()。

class AccountViewSet(viewsets.ModelViewSet):
    """
    用于查看和编辑帐户的简单 ViewSet。
    """
    queryset = Account.objects.all()
    serializer_class = AccountSerializer
    permission_classes = [IsAccountAdminOrReadOnly]

注意,可以使用 GenericAPIView 提供的任何标准属性或方法重写。例如,要动态确定它应该操作的查询集的 ViewSet,可以这样做:

class AccountViewSet(viewsets.ModelViewSet):
    """
    一个简单的视图集,用于查看和编辑与用户相关的帐户。
    """
    serializer_class = AccountSerializer
    permission_classes = [IsAccountAdminOrReadOnly]

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

注意,当从 ViewSet 中删除 queryset 属性后,任何关联的路由器将无法自动导出模型的 base_name,因此您必须指定 base_name kwarg 作为路由器注册的一部分。

4,ReadOnlyModelViewSet

ReadOnlyModelViewSet 类也继承自 GenericAPIView。与 ModelViewSet 一样,它还包括各种操作的实现,但与 ModelViewSet 不同,它只提供“只读”操作、.list().retrieve()

class AccountViewSet(viewsets.ReadOnlyModelViewSet):
    """
    用于查看帐户的简单 ViewSet。
    """
    queryset = Account.objects.all()
    serializer_class = AccountSerializer

猜你喜欢

转载自blog.csdn.net/dangfulin/article/details/108184764