• rest framework 之视图


    一、APIView

    APIView 直接继承 View(Django 内置的 View),也就是说 APIView 是最贴近原生 Django 的 View 的。

    因此可定制程度高,根据请求方法不同执行不同的函数:

    源码:

    class APIView(View):
    
        # The following policies may be set at either globally, or per-view.
        renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
        parser_classes = api_settings.DEFAULT_PARSER_CLASSES
        authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
        throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
        permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
        content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
        metadata_class = api_settings.DEFAULT_METADATA_CLASS
        versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
    
        # Allow dependency injection of other settings to make testing easier.
        settings = api_settings
    
        schema = DefaultSchema()
    
        @classmethod
        def as_view(cls, **initkwargs):
            """
            Store the original class on the view function.
    
            This allows us to discover information about the view when we do URL
            reverse lookups.  Used for breadcrumb generation.
            """
            ...
    

    使用方法

    1、urls.py

    from django.urls import path, re_path
    from api.views import UserView
    
    urlpatterns = [
        re_path('(?P<version>[v1|v2]+)/users/', UserView.as_view(), name='api_user'),
    ]
    

    2、views.py

    from django.shortcuts import render, HttpResponse
    from rest_framework.views import APIView
    
    
    class UserView(APIView):
    
        def get(self, request, *args, **kwargs):
           pass
    
        def post(self, request, *args, **kwargs):
            pass
    

    二、GenericViewSet

    映射关系

    GenericViewSet 在 URL 中采用映射的关系将请求方法于视图函数一一对应,具体如下图:

    URL 参数与视图函数映射关系

    参数 视图中方法 说明
    get list 获取数据
    post create 创建数据
    get retrieve 获取单条数据
    put update 更新
    patch partial_update 局部更新
    delete destroy 删除

    当然你也可以定位别的函数名,比如更新使用 add,相应地视图中也要定义为 def add() pass

    在使用时,URL 中对应了几组映射关系,相应地视图中也要重写相应函数/方法。


    GenericViewSet 继承关系

    GenericViewSet 继承 GenericAPIView

    class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
        """
        The GenericViewSet class does not provide any actions by default,
        but does include the base set of generic view behavior, such as
        the `get_object` and `get_queryset` methods.
        """
        pass
    

    GenericAPIView 中实现了以下方法,包括:获取数据源、序列化、分页等

    使用时,我们只需定义传入数据源、序列化的类和分页的类即可:

    from rest_framework.viewsets import GenericViewSet
    from .serializers import TestSerializers
    from rest_framework.pagination import PageNumberPagination
    
    
    class TestView(GenericViewSet):
        queryset = models.Role.objects.all()
        serializer_class = TestSerializers
        pagination_class = PageNumberPagination
    
        def list(self, request, *args, **kwargs):
            # 获取数据
            roles = self.get_queryset()
        
            # 分页
            pager_roles = self.paginate_queryset(roles)
        
            # 序列化
            roles_ser = self.get_serializer(instance=pager_roles, many=True)
        
            return Response(roles_ser.data)
    
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)
            # 验证用户输入的数据是否合法
            serializer.is_valid(raise_exception=True)
            # 合法则保存
            serializer.save()
    
            # headers
            # headers = self.get_success_headers(serializer.data)
    
            # 创建成功则返回刚刚创建的数据
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    
    
        def get_success_headers(self, data):
            try:
                return {'Location': str(data[api_settings.URL_FIELD_NAME])}     # URL_FIELD_NAME:url
            except (TypeError, KeyError):
                return {}
    

    其中 get_queryset()paginate_queryset()get_serializer() 都是 GenericViewSet 实现的方法,直接调用即可。


    如果你嫌每次还要写 list、create、delete 等方法,也可以直接继承 mixins.ListModelMixin、 mixins.CreateModelMixin、mixins.DestroyModelMixin ,其内部以及帮你实现了增删改查等功能,使用时只需继承即可。

    from rest_framework.viewsets import GenericViewSet
    from .serializers import TestSerializers
    from rest_framework.pagination import PageNumberPagination
    from rest_framework import mixins
    
    
    class TestView(mixins.ListModelMixin, mixins.CreateModelMixin,  GenericViewSet):
        # 只需将数据源于序列化的类和分页类赋值即可
        queryset = models.Role.objects.all()
        serializer_class = TestSerializers
        pagination_class = PageNumberPagination
    

    源码分析

    ListModelMixin 来看,是不是和我们之前自定义的 list 做的事一样,同样地它也是实现了查询数据、序列化和分页的功能:

    class ListModelMixin(object):
        """
        List a queryset.
        """
        def list(self, request, *args, **kwargs):
            queryset = self.filter_queryset(self.get_queryset())
    
            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)
    

    更多源码可参考:rest_framework/mixins.py


    参考

    1、urls.py

    from django.urls import path, re_path
    from api.views import TestView
    
    urlpatterns = [
        re_path('(?P<version>[v1|v2]+/test/)', TestView.as_view({'get': 'list', 'post': 'create'})),
    ]
    

    2、models.py

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework import viewsets
    from rest_framework.response import Response
    
    
    class TestView(viewsets.GenericViewSet):
        def list(self, request, *args, **kwargs):
            return Response('...')
    
        def add(self, request, *args, **kwargs):
            pass
    
        # def delete(self, request, *args, **kwargs):
        #     pass
    
        # def edit(self, request, *args, **kwargs):
        #     pass
    


    权限

    class GenericAPIView(views.APIView):
        """返回请求的对象"""
        def get_object(self):
            ...
            # May raise a permission denied
            self.check_object_permissions(self.request, obj)
            ...
            pass
    
    def check_object_permissions(self, request, obj):
        """
        Check if the request should be permitted for a given object.
        Raises an appropriate exception if the request is not permitted.
        检查是否应该允许给定对象的请求。
        如果不允许请求,则引发适当的异常。
        """
        for permission in self.get_permissions():
            if not permission.has_object_permission(request, self, obj):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )
    

    三、ModelViewSet

    ModelViewSet 是目前 rest_framework 中最高级别的 View,它继承了:

    class ModelViewSet(mixins.CreateModelMixin,
                       mixins.RetrieveModelMixin,
                       mixins.UpdateModelMixin,
                       mixins.DestroyModelMixin,
                       mixins.ListModelMixin,
                       GenericViewSet):
        """
        A viewset that provides default `create()`, `retrieve()`, `update()`,
        `partial_update()`, `destroy()` and `list()` actions.
        """
        pass
    

    因此只要继承 ModelViewSet 即可完成增删改查。

    1、urls.py

    from django.urls import path, re_path
    from api.views import TestView, TestView1
    
    urlpatterns = [
        re_path('(?P<version>[v1|v2]+/test/)', TestView.as_view({'get': 'list', 'post': 'create'})),
    
        re_path('(?P<version>[v1|v2]+/test1/)', TestView1.as_view({'get': 'list', 'post': 'create'})),
    
        #  re_path('(?P<version>[v1|v2]+/test1/(?P<pk>d+)/)', TestView1.as_view({'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
    ]
    

    2、views.py

    from rest_framework.viewsets import GenericViewSet
    from .serializers import TestSerializers
    from rest_framework.pagination import PageNumberPagination
    from rest_framework.viewsets import ModelViewSet
    
    
    class TestView1(ModelViewSet):
        queryset = models.Role.objects.all()
        serializer_class = TestSerializers
        pagination_class = PageNumberPagination
    

    四、总结

    • 如果想快速实现增删改查:ModelViewSet
    • 增删:CreateModelMixinDestroyModelMixinGenericViewSet
    • 复杂逻辑:GenericViewSetAPIView
  • 相关阅读:
    Django ListView实现分页
    redis-pipeline
    MRO
    进程状态
    ORM基本操作回顾
    协程回顾
    线程的回顾
    multiprocessing- 基于进程的并行性
    Fix Curl client hung issue
    Curl request 'Expect: 100-Continue' Issues and Risks
  • 原文地址:https://www.cnblogs.com/midworld/p/11380191.html
Copyright © 2020-2023  润新知