一、层次结构

  1. GenericViewSet(ViewSetMixin, generics.GenericAPIView) ---DRF
  2. GenericAPIView(views.APIView) ---DRF
  3. APIView(View) ---DRF
  4. View ---Django

第一阶:

  1. # 第一阶:
  2. ViewSetMixin
  3. ViewSet(ViewSetMixin, views.APIView)
  4. GenericViewSet(ViewSetMixin, generics.GenericAPIView)
  5. ModelViewSet(mixins.{C/R/U/D/L}ModelMixin, GenericViewSet)
  6. ReadOnlyModelViewSet(mixins.{R/L}ModelMixin, GenericViewSet)
  1. 事实上,除了ViewSetMixin以外,剩余的4个同阶类的内容都为空(只有PASS),ViewSetMixin增加了什么行为,后续再解释。

第二阶:

  1. # 第二阶:
  2. GenericAPIView(views.APIView)
  3. CreateAPIView
  4. ListAPIView
  5. RetrieveAPIView
  6. DestroyAPIView
  7. UpdateAPIView
  8. ListCreateAPIView
  9. RetrieveUpdateAPIView
  10. RetrieveDestroyAPIView
  11. RetrieveUpdateDestroyAPIView
  1. GenericAPIView以外,剩余的同阶类,实质上是GenericAPIViewmixins.{CRUDL}ModelMixin的组合继承。
    在类中,通过重写相应的HTTP方法(getputdelete等),调用mixis.{CRUDL}ModelView中的createlistretrieve等方法。
  1. # Concrete view classes that provide method handlers
  2. # by composing the mixin classes with the base view.
  3.  
  4. class CreateAPIView(mixins.CreateModelMixin,
  5. GenericAPIView):
  6. """
  7. Concrete view for creating a model instance.
  8. """
  9. def post(self, request, *args, **kwargs):
  10. return self.create(request, *args, **kwargs)
  11.  
  12. # 以上为CreateAPIView源代码

第三阶:

  1. # 第三阶
  2. APIView(View)
  1. APIView(View)为独一阶,地位非常独特。

第四阶:

  1. # 第四阶
  2. ContextMixin
  3. View
  4. TemplateResponseMixin
  5. TemplateView(TemplateResponseMixin, ContextMixin, View)
  6. RedirectView(View)
  1. 第四阶由Django提供,较为底层,一般很少去关注和使用,这里也不展开做详尽分析。

二、View、APIView、GenericAPIView、GenericViewSet的差别

1. Django View

如果使用Django自带的View,获取课程列表,代码大致是这样的:
  1. import json
  2. from django.views.generic.base import View
  3. from django.core import serializers
  4. from django.http import JsonResponse
  5. from .models import Course
  6.  
  7. class CourseListView(View):
  8. def get(self, request):
  9. """
  10. 通过Django的View实现课程列表页
  11. """
  12. courses = Course.objects.all()[:10]
  13. json_data = serializers.serialize('json', Courses)
  14. json_data = json.loads(json_data)
  15. return JsonResponse(json_data, safe=False)
  16.  
  17. # 上述代码使用Django自身的模块,返回application/json的数据,可以返回HttpResponse,也可以是其子类JsonResponse
  18. # 在Django中也有serializers,不同于DRF的serializers,它只能对基本类型进行JSON序列化、反序列化

这是一个普通的CBV,Django通过as_view和dispatch函数,将request请求传递给get(self, request)方法,从而返回response。

关于这部分的内容,可以参考 基于类的视图

2. APIView

如果用APIView来实现,代码类似于:
  1. from rest_framework.views import APIView
  2. from rest_framework.response import Response
  3. from .serializers import CourseSerializer
  4.  
  5. class CourseListView(APIView):
  6. def get(self, request, format=None):
  7. """
  8. 通过APIView实现课程列表页
  9. """
  10. courses = Course.objects.all()
  11. serializer = CourseSerializer(courses, many=True)
  12. return Response(serializer.data)
  13.  
  14. # 在APIView这个例子中,调用了drf本身的serializer和Response。

APIViewView的不同之处在于:

  • 请求和返回使用的DRF的Request、Response,而不是Django的HttpRequest、HttpResponse;
  • 任何APIException异常都会被捕获到,并且处理成合适的响应信息;
  • 在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。

支持定义的属性:

  • authentication_classes  列表或元组,身份认证类
  • permissoin_classes       列表或元组,权限检查类
  • throttle_classes            列表或元组,流量控制类

3. GenericAPIView

如果用GenericAPIView实现,代码类似于:
  1. # url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()),
  2.  
  3. class BookDetailView(GenericAPIView):
  4. queryset = BookInfo.objects.all()
  5. serializer_class = BookInfoSerializer
  6.  
  7. def get(self, request, pk):
  8. book = self.get_object()
  9. serializer = self.get_serializer(book)
  10. return Response(serializer.data)

由于mixins.{CRUDL}ModelMixin的存在,我们往往可以这么写,

  1. from rest_framework import mixins
  2. from rest_framework import generics
  3.  
  4. class CourseListView(mixins.ListModelMixin, generics.GenericAPIView):
  5. """
  6. 课程列表页
  7. """
  8. queryset = Course.objects.all()
  9. serialize_class = CourseSerializer
  10.  
  11. def get(self, request, *args, **kwargs):
  12. # list方法是存在于mixins中的,同理,create等等也是,GenericAPIView没有这些方法!
  13. return self.list(request, *args, **kwargs)

  14. # 如果我们直接继承ListAPIView(mixins.ListModelMixin, GenericAPIView),那么def get(...)方法都可以不写
  15. class CourseListView(ListAPIView):
  16. """
  17. 课程列表页
  18. """
  19. queryset = Course.objects.all()
  20. serialize_class = CourseSerializer

支持定义的属性:

  • queryset                 指定queryset
  • serializer_class        指定serializer
  • pagination_class      指定分页类
  • filter_backends        指定过滤类
  • lookup_field            查询单一数据库对象时使用的条件字段,默认为'pk'
  • lookup_url_kwarg     查询单一数据时URL中的参数关键字名称,默认与look_field相同

提供的方法:

  • get_queryset(self)
    • 通过访问self.queryset,获取queryset,两者一般择其一;
  1. def get_queryset(self):
  2. """
  3. Get the list of items for this view.
  4. This must be an iterable, and may be a queryset.
  5. Defaults to using `self.queryset`.
  6.  
  7. This method should always be used rather than accessing `self.queryset`
  8. directly, as `self.queryset` gets evaluated only once, and those results
  9. are cached for all subsequent requests.
  10.  
  11. You may want to override this if you need to provide different
  12. querysets depending on the incoming request.
  13.  
  14. (Eg. return a list of items that is specific to the user)
  15. """
  16. assert self.queryset is not None, (
  17. "'%s' should either include a `queryset` attribute, "
  18. "or override the `get_queryset()` method."
  19. % self.__class__.__name__
  20. )
  21.  
  22. queryset = self.queryset
  23. if isinstance(queryset, QuerySet):
  24. # Ensure queryset is re-evaluated on each request.
  25. queryset = queryset.all()
  26. return queryset
  • get_serializer_class(self)

    • 通过访问self.serializer_class,获取serializer_class,两者一般择其一;
  1. def get_serializer_class(self):
  2. """
  3. Return the class to use for the serializer.
  4. Defaults to using `self.serializer_class`.
  5.  
  6. You may want to override this if you need to provide different
  7. serializations depending on the incoming request.
  8.  
  9. (Eg. admins get full serialization, others get basic serialization)
  10. """
  11. assert self.serializer_class is not None, (
  12. "'%s' should either include a `serializer_class` attribute, "
  13. "or override the `get_serializer_class()` method."
  14. % self.__class__.__name__
  15. )
  16.  
  17. return self.serializer_class
  • get_serializer(self, args, *kwargs)

    • 如果我们在View中,想要获取serializer instance,可以直接调用此方法。
  • get_serializer_context(self)
    • 创建request、format、view三个数据对象,作为serializer实例化时的context属性;
  1. def get_serializer(self, *args, **kwargs):
  2. """
  3. Return the serializer instance that should be used for validating and
  4. deserializing input, and for serializing output.
  5. """
  6. serializer_class = self.get_serializer_class()
  7. kwargs['context'] = self.get_serializer_context()
  8. return serializer_class(*args, **kwargs)
  9.  
  10. def get_serializer_context(self):
  11. """
  12. Extra context provided to the serializer class.
  13. """
  14. return {
  15. 'request': self.request,
  16. 'format': self.format_kwarg,
  17. 'view': self
  18. }
  • get_object(self)

    • 该方法对queryset进行过滤操作,返回的obj供view显示。如果你需要非标准的过滤操作,可以重写该方法;
  1. def get_object(self):
  2. """
  3. Returns the object the view is displaying.
  4.  
  5. You may want to override this if you need to provide non-standard
  6. queryset lookups. Eg if objects are referenced using multiple
  7. keyword arguments in the url conf.
  8. """
  9. queryset = self.filter_queryset(self.get_queryset())
  10.  
  11. # Perform the lookup filtering.
  12. lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
  13.  
  14. assert lookup_url_kwarg in self.kwargs, (
  15. 'Expected view %s to be called with a URL keyword argument '
  16. 'named "%s". Fix your URL conf, or set the `.lookup_field` '
  17. 'attribute on the view correctly.' %
  18. (self.__class__.__name__, lookup_url_kwarg)
  19. )
  20.  
  21. filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
  22. obj = get_object_or_404(queryset, **filter_kwargs)
  23.  
  24. # May raise a permission denied
  25. self.check_object_permissions(self.request, obj)
  26.  
  27. return obj
  • filter_queryset()

    • 将filter_backends的过滤类,应用到queryset上;
  1. def filter_queryset(self, queryset):
  2. """
  3. Given a queryset, filter it with whichever filter backend is in use.
  4.  
  5. You are unlikely to want to override this method, although you may need
  6. to call it either from a list view, or from a custom `get_object`
  7. method if you want to apply the configured filtering backend to the
  8. default queryset.
  9. """
  10. for backend in list(self.filter_backends):
  11. queryset = backend().filter_queryset(self.request, queryset, self)
  12. return queryset

4. GenericViewSet

GenericViewSet(ViewSetMixin, generics.GenericAPIView),实际上等于ViewSetMixin + GenericAPIView,而ViewSetMixin的主要工作,就是重写了as_view方法。
 
  1. class ViewSetMixin(object):
  2. """
  3. This is the magic.
  4.  
  5. Overrides `.as_view()` so that it takes an `actions` keyword that performs
  6. the binding of HTTP methods to actions on the Resource.
  7.  
  8. For example, to create a concrete view binding the 'GET' and 'POST' methods
  9. to the 'list' and 'create' actions...
  10.  
  11. view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
  12. """

我们回过头来看,上述不同视图在接受web请求时,as_view和CBV方法(get、put、post等)的协同工作方式是不同的:

View:

  • URL映射至CBV的as_view方法,该方法通过调用dispatch方法,负责HTTP请求和CBV方法之间的映射(POST to post、GET to get、PUT to put);

APIView:

  • 同上

GenericAPIView:

  • 同上,区别是通过mixin.{CRUDL}ModelMixin引入了action的概念,可以手动或者自动地在get/put/post等方法中调用list/create/retrieve等action

GenericViewSet

  • 重写了as_view方法,支持类似 MyViewSet.as_view({'get': 'list', 'post': 'create'}) 的动态绑定功能,或者由router.register进行注册;

如何选择:

  1. 如果使用router.register进行URL请求的注册与绑定,建议使用GenericViewSet,最为高效、规范、合理;
  2. 如果需要重构原有的FBV,建议使用GenericAPIView,改动小、变动少;

PS:

 

DRF的APIView、GenericAPIView、GenericViewSet的原理分析的更多相关文章

  1. DRF框架(五)——context传参,二次封装Response类,两个视图基类(APIView/GenericAPIView),视图扩展类(mixins),子类视图(工具视图),视图集(viewsets),工具视图集

    复习 1.整体修改与局部修改 # 序列化get (给前端传递参数) #查询 ser_obj = ModelSerializer(model_obj) #只传递一个参数,默认是instance的参数,查 ...

  2. Handler系列之原理分析

    上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...

  3. Java NIO使用及原理分析(1-4)(转)

    转载的原文章也找不到!从以下博客中找到http://blog.csdn.net/wuxianglong/article/details/6604817 转载自:李会军•宁静致远 最近由于工作关系要做一 ...

  4. 原子类java.util.concurrent.atomic.*原理分析

    原子类java.util.concurrent.atomic.*原理分析 在并发编程下,原子操作类的应用可以说是无处不在的.为解决线程安全的读写提供了很大的便利. 原子类保证原子的两个关键的点就是:可 ...

  5. Android中Input型输入设备驱动原理分析(一)

    转自:http://blog.csdn.net/eilianlau/article/details/6969361 话说Android中Event输入设备驱动原理分析还不如说Linux输入子系统呢,反 ...

  6. 转载:AbstractQueuedSynchronizer的介绍和原理分析

    简介 提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架.该同步器(以下简称同步器)利用了一个int来表示状态,期望它能够成为实现大部分同步需求的基础.使用的方法是继承,子类通过 ...

  7. Camel运行原理分析

    Camel运行原理分析 以一个简单的例子说明一下camel的运行原理,例子本身很简单,目的就是将一个目录下的文件搬运到另一个文件夹,处理器只是将文件(限于文本文件)的内容打印到控制台,首先代码如下: ...

  8. NOR Flash擦写和原理分析

    NOR Flash擦写和原理分析 1. NOR FLASH 的简单介绍 NOR FLASH 是很常见的一种存储芯片,数据掉电不会丢失.NOR FLASH支持Execute On Chip,即程序可以直 ...

  9. 使用AsyncTask异步更新UI界面及原理分析

    概述: AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类.AsyncTask的内部实现是一个线程池,所有提交的异步任务都会在这个线程池中的工作线 ...

随机推荐

  1. Bean的一生(Bean的生命周期)

    1. 什么是Bean? Bean是spring中组成应用程序的主体及由spring IoC容器所管理的对象(IoC容器初始化.装配及管理的对象).如果把spring比作一座大型工厂,那么bean就是该 ...

  2. jackson springboot配置时间格式

    yml文件中这样进行配置 spring: jackson: date-format: yyyy-MM-dd HH:mm:ss spring.jackson.date-format指定日期格式,比如yy ...

  3. 【AICC】2019训练营笔记

    1.AI 人工的方法在机器上实现智能:机器学习.计算机视觉.规划决策.自然语言处理.认知推理.高效搜索 2.三大学派 符号主义 连接主义:CNN 行为主义 3.两条路线 结构模仿 功能模仿 4.AI芯 ...

  4. 07-numpy-笔记-join

    字符串.join(字符串序列) 一目了然: #!/usr/bin/python # -*- coding: UTF-8 -*- str = "-"; seq = ("a& ...

  5. Checkout 显示 URL /../../.. 不存在

    Checkout 显示 URL /../../.. 不存在 Checkout 显示 URL /../../.. 不存在 如果库的路径是 svn库的路径为:/usr/local/svn/test/ 启动 ...

  6. Angle Beats Gym - 102361A(计算几何)

    Angle Beats \[ Time Limit: 4000 ms \quad Memory Limit: 1048576 kB \] 题意 给出 \(n\) 个初始点以及 \(q\) 次询问,每次 ...

  7. android开发环境sdk manager无法更新问题

    由于无法fq,也没有vpn,建议各位新手不用sdk manager去安装,直接下载bundle包,就不用去折腾各种开发环境了.推荐bundle下载地址:http://adt.android-studi ...

  8. selenium--单选框和复选框的操作

    单选框操作 from selenium import webdriver import unittest class Test_radio(unittest.TestCase): def test_S ...

  9. 图的遍历 | 1076 bfs

    bfs踩了很多坑才写完.注意:出队时不做是否vis判断,但是要加上vis[出队顶点]=1 .入队时进行判断,并且也要 vis[入队顶点]=1 #include <stdio.h> #inc ...

  10. 两次bfs求树的直径的正确性

    结论:离树上任意点\(u\)最远的点一定是这颗树直径的一个端点. 证明: 若点\(u\)在树的直径上,设它与直径两个端点\(x,y\)的距离分别为\(S1\).\(S2\),若距离其最远的点\(v\) ...