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

posted @ 2020-09-06 22:28  Hubery_Jun  阅读(135)  评论(0编辑  收藏  举报