DRF的APIView、GenericAPIView、GenericViewSet的原理分析
一、层次结构
- GenericViewSet(ViewSetMixin, generics.GenericAPIView) ---DRF
- GenericAPIView(views.APIView) ---DRF
- APIView(View) ---DRF
- View ---Django
第一阶:
- # 第一阶:
- ViewSetMixin
- ViewSet(ViewSetMixin, views.APIView)
- GenericViewSet(ViewSetMixin, generics.GenericAPIView)
- ModelViewSet(mixins.{C/R/U/D/L}ModelMixin, GenericViewSet)
- ReadOnlyModelViewSet(mixins.{R/L}ModelMixin, GenericViewSet)
- 事实上,除了ViewSetMixin以外,剩余的4个同阶类的内容都为空(只有PASS),ViewSetMixin增加了什么行为,后续再解释。
第二阶:
- # 第二阶:
- GenericAPIView(views.APIView)
- CreateAPIView
- ListAPIView
- RetrieveAPIView
- DestroyAPIView
- UpdateAPIView
- ListCreateAPIView
- RetrieveUpdateAPIView
- RetrieveDestroyAPIView
- RetrieveUpdateDestroyAPIView
- 除GenericAPIView以外,剩余的同阶类,实质上是GenericAPIView与mixins.{CRUDL}ModelMixin的组合继承。
在类中,通过重写相应的HTTP方法(get、put、delete等),调用mixis.{CRUDL}ModelView中的create、list、retrieve等方法。
- # Concrete view classes that provide method handlers
- # by composing the mixin classes with the base view.
- class CreateAPIView(mixins.CreateModelMixin,
- GenericAPIView):
- """
- Concrete view for creating a model instance.
- """
- def post(self, request, *args, **kwargs):
- return self.create(request, *args, **kwargs)
- # 以上为CreateAPIView源代码
第三阶:
- # 第三阶
- APIView(View)
- APIView(View)为独一阶,地位非常独特。
第四阶:
- # 第四阶
- ContextMixin
- View
- TemplateResponseMixin
- TemplateView(TemplateResponseMixin, ContextMixin, View)
- RedirectView(View)
- 第四阶由Django提供,较为底层,一般很少去关注和使用,这里也不展开做详尽分析。
二、View、APIView、GenericAPIView、GenericViewSet的差别
1. Django View
- import json
- from django.views.generic.base import View
- from django.core import serializers
- from django.http import JsonResponse
- from .models import Course
- class CourseListView(View):
- def get(self, request):
- """
- 通过Django的View实现课程列表页
- """
- courses = Course.objects.all()[:10]
- json_data = serializers.serialize('json', Courses)
- json_data = json.loads(json_data)
- return JsonResponse(json_data, safe=False)
- # 上述代码使用Django自身的模块,返回application/json的数据,可以返回HttpResponse,也可以是其子类JsonResponse
- # 在Django中也有serializers,不同于DRF的serializers,它只能对基本类型进行JSON序列化、反序列化
这是一个普通的CBV,Django通过as_view和dispatch函数,将request请求传递给get(self, request)方法,从而返回response。
关于这部分的内容,可以参考 基于类的视图
2. APIView
- from rest_framework.views import APIView
- from rest_framework.response import Response
- from .serializers import CourseSerializer
- class CourseListView(APIView):
- def get(self, request, format=None):
- """
- 通过APIView实现课程列表页
- """
- courses = Course.objects.all()
- serializer = CourseSerializer(courses, many=True)
- return Response(serializer.data)
- # 在APIView这个例子中,调用了drf本身的serializer和Response。
APIView与View的不同之处在于:
- 请求和返回使用的DRF的
Request、
Response,
而不是Django的HttpRequest、
HttpResponse;
- 任何APIException异常都会被捕获到,并且处理成合适的响应信息;
- 在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
支持定义的属性:
- authentication_classes 列表或元组,身份认证类
- permissoin_classes 列表或元组,权限检查类
- throttle_classes 列表或元组,流量控制类
3. GenericAPIView
- # url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()),
- class BookDetailView(GenericAPIView):
- queryset = BookInfo.objects.all()
- serializer_class = BookInfoSerializer
- def get(self, request, pk):
- book = self.get_object()
- serializer = self.get_serializer(book)
- return Response(serializer.data)
由于mixins.{CRUDL}ModelMixin的存在,我们往往可以这么写,
- from rest_framework import mixins
- from rest_framework import generics
- class CourseListView(mixins.ListModelMixin, generics.GenericAPIView):
- """
- 课程列表页
- """
- queryset = Course.objects.all()
- serialize_class = CourseSerializer
- def get(self, request, *args, **kwargs):
- # list方法是存在于mixins中的,同理,create等等也是,GenericAPIView没有这些方法!
- return self.list(request, *args, **kwargs)
- # 如果我们直接继承ListAPIView(mixins.ListModelMixin, GenericAPIView),那么def get(...)方法都可以不写
- class CourseListView(ListAPIView):
- """
- 课程列表页
- """
- queryset = Course.objects.all()
- 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,两者一般择其一;
- def get_queryset(self):
- """
- Get the list of items for this view.
- This must be an iterable, and may be a queryset.
- Defaults to using `self.queryset`.
- This method should always be used rather than accessing `self.queryset`
- directly, as `self.queryset` gets evaluated only once, and those results
- are cached for all subsequent requests.
- You may want to override this if you need to provide different
- querysets depending on the incoming request.
- (Eg. return a list of items that is specific to the user)
- """
- assert self.queryset is not None, (
- "'%s' should either include a `queryset` attribute, "
- "or override the `get_queryset()` method."
- % self.__class__.__name__
- )
- queryset = self.queryset
- if isinstance(queryset, QuerySet):
- # Ensure queryset is re-evaluated on each request.
- queryset = queryset.all()
- return queryset
- get_serializer_class(self)
- 通过访问self.serializer_class,获取serializer_class,两者一般择其一;
- def get_serializer_class(self):
- """
- Return the class to use for the serializer.
- Defaults to using `self.serializer_class`.
- You may want to override this if you need to provide different
- serializations depending on the incoming request.
- (Eg. admins get full serialization, others get basic serialization)
- """
- assert self.serializer_class is not None, (
- "'%s' should either include a `serializer_class` attribute, "
- "or override the `get_serializer_class()` method."
- % self.__class__.__name__
- )
- return self.serializer_class
- get_serializer(self, args, *kwargs)
- 如果我们在View中,想要获取serializer instance,可以直接调用此方法。
- get_serializer_context(self)
- 创建request、format、view三个数据对象,作为serializer实例化时的context属性;
- def get_serializer(self, *args, **kwargs):
- """
- Return the serializer instance that should be used for validating and
- deserializing input, and for serializing output.
- """
- serializer_class = self.get_serializer_class()
- kwargs['context'] = self.get_serializer_context()
- return serializer_class(*args, **kwargs)
- def get_serializer_context(self):
- """
- Extra context provided to the serializer class.
- """
- return {
- 'request': self.request,
- 'format': self.format_kwarg,
- 'view': self
- }
- get_object(self)
- 该方法对queryset进行过滤操作,返回的obj供view显示。如果你需要非标准的过滤操作,可以重写该方法;
- def get_object(self):
- """
- Returns the object the view is displaying.
- You may want to override this if you need to provide non-standard
- queryset lookups. Eg if objects are referenced using multiple
- keyword arguments in the url conf.
- """
- queryset = self.filter_queryset(self.get_queryset())
- # Perform the lookup filtering.
- lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
- assert lookup_url_kwarg in self.kwargs, (
- 'Expected view %s to be called with a URL keyword argument '
- 'named "%s". Fix your URL conf, or set the `.lookup_field` '
- 'attribute on the view correctly.' %
- (self.__class__.__name__, lookup_url_kwarg)
- )
- filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
- obj = get_object_or_404(queryset, **filter_kwargs)
- # May raise a permission denied
- self.check_object_permissions(self.request, obj)
- return obj
- filter_queryset()
- 将filter_backends的过滤类,应用到queryset上;
- def filter_queryset(self, queryset):
- """
- Given a queryset, filter it with whichever filter backend is in use.
- You are unlikely to want to override this method, although you may need
- to call it either from a list view, or from a custom `get_object`
- method if you want to apply the configured filtering backend to the
- default queryset.
- """
- for backend in list(self.filter_backends):
- queryset = backend().filter_queryset(self.request, queryset, self)
- return queryset
4. GenericViewSet
- class ViewSetMixin(object):
- """
- This is the magic.
- Overrides `.as_view()` so that it takes an `actions` keyword that performs
- the binding of HTTP methods to actions on the Resource.
- For example, to create a concrete view binding the 'GET' and 'POST' methods
- to the 'list' and 'create' actions...
- view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
- """
我们回过头来看,上述不同视图在接受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进行注册;
如何选择:
- 如果使用router.register进行URL请求的注册与绑定,建议使用GenericViewSet,最为高效、规范、合理;
- 如果需要重构原有的FBV,建议使用GenericAPIView,改动小、变动少;
PS:
DRF的APIView、GenericAPIView、GenericViewSet的原理分析的更多相关文章
- DRF框架(五)——context传参,二次封装Response类,两个视图基类(APIView/GenericAPIView),视图扩展类(mixins),子类视图(工具视图),视图集(viewsets),工具视图集
复习 1.整体修改与局部修改 # 序列化get (给前端传递参数) #查询 ser_obj = ModelSerializer(model_obj) #只传递一个参数,默认是instance的参数,查 ...
- Handler系列之原理分析
上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...
- Java NIO使用及原理分析(1-4)(转)
转载的原文章也找不到!从以下博客中找到http://blog.csdn.net/wuxianglong/article/details/6604817 转载自:李会军•宁静致远 最近由于工作关系要做一 ...
- 原子类java.util.concurrent.atomic.*原理分析
原子类java.util.concurrent.atomic.*原理分析 在并发编程下,原子操作类的应用可以说是无处不在的.为解决线程安全的读写提供了很大的便利. 原子类保证原子的两个关键的点就是:可 ...
- Android中Input型输入设备驱动原理分析(一)
转自:http://blog.csdn.net/eilianlau/article/details/6969361 话说Android中Event输入设备驱动原理分析还不如说Linux输入子系统呢,反 ...
- 转载:AbstractQueuedSynchronizer的介绍和原理分析
简介 提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架.该同步器(以下简称同步器)利用了一个int来表示状态,期望它能够成为实现大部分同步需求的基础.使用的方法是继承,子类通过 ...
- Camel运行原理分析
Camel运行原理分析 以一个简单的例子说明一下camel的运行原理,例子本身很简单,目的就是将一个目录下的文件搬运到另一个文件夹,处理器只是将文件(限于文本文件)的内容打印到控制台,首先代码如下: ...
- NOR Flash擦写和原理分析
NOR Flash擦写和原理分析 1. NOR FLASH 的简单介绍 NOR FLASH 是很常见的一种存储芯片,数据掉电不会丢失.NOR FLASH支持Execute On Chip,即程序可以直 ...
- 使用AsyncTask异步更新UI界面及原理分析
概述: AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类.AsyncTask的内部实现是一个线程池,所有提交的异步任务都会在这个线程池中的工作线 ...
随机推荐
- Bean的一生(Bean的生命周期)
1. 什么是Bean? Bean是spring中组成应用程序的主体及由spring IoC容器所管理的对象(IoC容器初始化.装配及管理的对象).如果把spring比作一座大型工厂,那么bean就是该 ...
- jackson springboot配置时间格式
yml文件中这样进行配置 spring: jackson: date-format: yyyy-MM-dd HH:mm:ss spring.jackson.date-format指定日期格式,比如yy ...
- 【AICC】2019训练营笔记
1.AI 人工的方法在机器上实现智能:机器学习.计算机视觉.规划决策.自然语言处理.认知推理.高效搜索 2.三大学派 符号主义 连接主义:CNN 行为主义 3.两条路线 结构模仿 功能模仿 4.AI芯 ...
- 07-numpy-笔记-join
字符串.join(字符串序列) 一目了然: #!/usr/bin/python # -*- coding: UTF-8 -*- str = "-"; seq = ("a& ...
- Checkout 显示 URL /../../.. 不存在
Checkout 显示 URL /../../.. 不存在 Checkout 显示 URL /../../.. 不存在 如果库的路径是 svn库的路径为:/usr/local/svn/test/ 启动 ...
- Angle Beats Gym - 102361A(计算几何)
Angle Beats \[ Time Limit: 4000 ms \quad Memory Limit: 1048576 kB \] 题意 给出 \(n\) 个初始点以及 \(q\) 次询问,每次 ...
- android开发环境sdk manager无法更新问题
由于无法fq,也没有vpn,建议各位新手不用sdk manager去安装,直接下载bundle包,就不用去折腾各种开发环境了.推荐bundle下载地址:http://adt.android-studi ...
- selenium--单选框和复选框的操作
单选框操作 from selenium import webdriver import unittest class Test_radio(unittest.TestCase): def test_S ...
- 图的遍历 | 1076 bfs
bfs踩了很多坑才写完.注意:出队时不做是否vis判断,但是要加上vis[出队顶点]=1 .入队时进行判断,并且也要 vis[入队顶点]=1 #include <stdio.h> #inc ...
- 两次bfs求树的直径的正确性
结论:离树上任意点\(u\)最远的点一定是这颗树直径的一个端点. 证明: 若点\(u\)在树的直径上,设它与直径两个端点\(x,y\)的距离分别为\(S1\).\(S2\),若距离其最远的点\(v\) ...