zoukankan      html  css  js  c++  java
  • DRF 视图源码分析

    1. mixin 和 viewsets

    drf 视图中用到最多的(继承)就是 viewsets.GenericViewSetmixin 中的五个方法,视图主要可分为以下几类:

    GenericViewSet(viewsets)    # drf 最高级
    GenericAPIView      # drf
    APIView     # drf
    View        # django 自带
    

    每个 view 的功能也不尽相同,主要是因为 mixin 的存在,mixin 总共分为五个通用类视图,每个都与模型相关:

    • CreateModelMixin:新增单条
    • ListModelMixin:获取多个
    • UpdateModelMixin:更新单个
    • RetrieveModelMixin:获取单个(详情)
    • DestoryModelMixin:删除单个

    可以说这五个类涉及到了模型相关的所有操作:增删改查,还有数据的序列化、分页等操作,继承它们可以免去自己写的烦恼。

    mixin.py 源码:

    """
    Basic building blocks for generic class based views.
    
    We don't bind behaviour to http method handlers yet,
    which allows mixin classes to be composed in interesting ways.
    """
    from rest_framework import status
    from rest_framework.response import Response
    from rest_framework.settings import api_settings
    
    
    class CreateModelMixin:
        """
        Create a model instance. 创建一个模型实例
        """
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    
        def perform_create(self, serializer):
            serializer.save()
    
        def get_success_headers(self, data):
            try:
                return {'Location': str(data[api_settings.URL_FIELD_NAME])}
            except (TypeError, KeyError):
                return {}
    
    
    class ListModelMixin:
        """
        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)
    
    
    class RetrieveModelMixin:
        """
        Retrieve a model instance.  检索模型实例
        """
        def retrieve(self, request, *args, **kwargs):
            instance = self.get_object()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)
    
    
    class UpdateModelMixin:
        """
        Update a model instance.    更新模型实例
        """
        def update(self, request, *args, **kwargs):
            partial = kwargs.pop('partial', False)
            instance = self.get_object()
            serializer = self.get_serializer(instance, data=request.data, partial=partial)
            serializer.is_valid(raise_exception=True)
            self.perform_update(serializer)
    
            if getattr(instance, '_prefetched_objects_cache', None):
                # If 'prefetch_related' has been applied to a queryset, we need to
                # forcibly invalidate the prefetch cache on the instance.
                instance._prefetched_objects_cache = {}
    
            return Response(serializer.data)
    
        def perform_update(self, serializer):
            serializer.save()
    
        def partial_update(self, request, *args, **kwargs):
            kwargs['partial'] = True
            return self.update(request, *args, **kwargs)
    
    
    class DestroyModelMixin:
        """
        Destroy a model instance.   销毁一个模型实例
        """
        def destroy(self, request, *args, **kwargs):
            instance = self.get_object()
            self.perform_destroy(instance)
            return Response(status=status.HTTP_204_NO_CONTENT)
    
        def perform_destroy(self, instance):
            instance.delete()
    

    示例对比

    以下均为获取商品列表数据,视图函数分别继承的是APIView 和 ``

    1、使用 APIView

    class GoodsListView(APIView):
        """
        商品列表
        """
    
        def get(self, request, format=None):
            goods = Goods.objects.all()
            goods_serilizer = GoodsSerializer(goods, many=True)
            return Response(goods_serilizer.data)
    

    2、使用 ListModelMixin 和 GenericViewSet

    class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
        """
        商品列表页
        """
        queryset = Goods.objects.all().order_by('id')
        serializer_class = GoodsSerializer
    

    因为 ListModelMixin 内部实现了数据查询集(商品列表数据查询)、分页以及序列化,在使用时只需重写即可。

    class ListModelMixin:
        """
        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)
    

    这里采用的是 mixin 混入类,ListModelMixin 中并没有 get_queryset()、paginate_queryset() 和 get_serializer() 方法,而是调用的 GenericViewSet 中的相关方法。

    2. Request 和 Response 对象

    drf 对原有 DjangoHttpRequest、HttpResponse 做了相应的增强,使用起来更加灵活。

    2.1 Request 对象

    常用方法或属性

    方法 作用 说明
    request.data 返回请求的解析内容 可解析文件、非文件(non-file inputs)、POST 以外的请求方式(如 PUT、PATCH)等
    .query_params 类似于 request.GET 推荐使用,不仅适用 GET 请求,其他请求方式也适用
    .parser 解析器 通常不需要关注

    2.2 Response 对象

    Response 对象可以根据客户端请求返回不同的数据格式(如:JSON、HTML、FORM 表单之类)。

    Response 类使用的渲染器不能处理复杂的数据类型(如:Django 模型实例),需要在创建 Response 对象之前将数据序列化为基本的数据类型(如:drfSerializer 类,或其他)。

    Response 构造方法:

    Response(data, status=None, template_name=None, headers=None, content_type=None)
    
    • data:响应的序列化数据
    • status:响应状态码,默认 200
    • template_name:选择 HTMLRenderer 时使用的模板名称
    • headers:设置 HTTP header 字典类型
    • content_type:响应的内存类型,通常渲染器会根据内容协商的结果自动设置,但有时也需要手动设置

    属性

    • .data:未渲染,但已序列化的响应数据
    • .status_code:状态码
    • .content:将会响应的内容,必须先调用 .render() 方法,才能访问 .content
    • .template_name:只有在 response 的渲染器是 HTMLRenderer 或其他自定义模板渲染器时才需要提供
    • .accepted_renderer:用于将会返回的响应内容的渲染器实例,从视图返回响应之前由 APIView@api_view 自动设置
    • .accepted_media_type:内容协商阶段选择的媒体类型,从视图返回响应之前由 APIView@api_view 自动设置
    • .renderer_context:将传递给渲染器的 .render() 方法的附加的上下文信息字典,视图返回响应之前由 APIView@api_view 自动设置

    3. 全自动路由 DefaultRouter

    1、urls.py

    from django.conf.urls import url, include
    from rest_framework import routers
    from api.views import TestView, TestView1
    
    
    router = routers.DefaultRouter()
    router.register(r'test1', TestView1)
    
    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/', include(router.urls)),
    ]
    

    2、views.py

    from rest_framework.viewsets import ModelViewSet
    from rest_framework import serializers
    from .. import models
    
    
    class UserSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo
            fields = "__all__"
    
    class TestView1(ModelViewSet):
        queryset = models.Role.objects.all()
        serializer_class = TestSerializers
        pagination_class = PageNumberPagination
    

    自定义路由:

    from django.conf.urls import url, include
    from rest_framework import routers
    from api.views import TestView, TestView1
    
    
    
    urlpatterns = [
        path('test1/', TestView1.as_view(), name='test1')
    ]
    

    参考文章:https://www.cnblogs.com/Mixtea/p/10561037.html

  • 相关阅读:
    Centos7下rc.local文件开机不执行…
    Centos7添加密码安全策略
    Java8 时间日期类操作
    XML配置spring session jdbc实现session共享
    Spring Boot 2.x以后static下面的静态资源被拦截
    外观模式
    组合设计模式
    Java线程池源码解析
    观察者模式
    Java使用POI解析Excel表格
  • 原文地址:https://www.cnblogs.com/midworld/p/13624031.html
Copyright © 2011-2022 走看看