手摸手,带你用Django REST Framework撸接口系列三(视图篇)
今天这边文章主要讲解Django REST framework视图,除了最初接触django会用到基于函数的视图,在真正项目开发中我们都会用到类视图,Django REST framework对我们的视图做了进一步的封装。
Django REST Framework系列文章
- 手摸手,带你用Django REST Framework撸接口系列一(基础篇)
- 手摸手,带你用Django REST Framework撸接口系列二(序列化器篇)
- 手摸手,带你用Django REST Framework撸接口系列三(视图篇)
- 手摸手,带你用Django REST Framework撸接口系列四(渲染器篇)
- 手摸手,带你用Django REST Framework撸接口系列五(路由篇)
- 手摸手,带你用Django REST Framework撸接口系列六(认证篇)
- 手摸手,带你用Django REST Framework撸接口系列七(权限篇)
- 手摸手,带你用Django REST Framework撸接口系列八(限流篇)
- 手摸手,带你用Django REST Framework撸接口系列九(过滤篇)
- 手摸手,带你用Django REST Framework撸接口系列十(排序篇)
- 手摸手,带你用Django REST Framework撸接口系列十一(分页篇)
- 手摸手,带你用Django REST Framework撸接口系列十二(异常处理篇)
- 手摸手,带你用Django REST Framework撸接口系列十三(自动生成接口文档篇)
请求与响应
在最开始接触Django REST framework的时候我们最先用到的就是APIView,它其实是直接继承了我们django自带的View类,在它的基础上对request进行了进一步的封装。
request
REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request类的对象。
REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典对象保存到Request对象中。
Request对象的数据是自动根据前端发送数据的格式进行解析之后的结果。
无论前端发送的哪种格式的数据,我们都可以以统一的方式读取数据。
常用属性
request.data
request.data
返回解析之后的请求体数据。类似于Django中标准的request.POST
和 request.FILES
属性,但提供如下特性:
- 包含了解析之后的文件和非文件数据
- 包含了对POST、PUT、PATCH请求方式解析后的数据
- 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
Request.query_params
request.query_params
与Django标准的request.GET
相同,只是更换了更正确的名称而已。
Response
模块位置:rest_framework.response.Response
REST framework提供了一个响应类Response
,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型。
构造方式
1 | Response(data, status=None, template_name=None, headers=None, content_type=None) |
data
数据不要是render处理之后的数据,只需传递python的内建类型数据即可,REST framework会使用renderer
渲染器处理data
(例如会渲染成前端可以识别的json格式)。
data
不能是复杂结构的数据,如Django的模型类对象,对于这样的数据我们可以使用Serializer
序列化器序列化处理后(转为了Python字典类型)再传递给data
参数。
参数说明:
data
: 为响应准备的序列化处理后的数据;status
: 状态码,默认200;template_name
: 模板名称,如果使用HTMLRenderer
时需指明;headers
: 用于存放响应头信息的字典;content_type
: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数。
状态码
为了方便设置状态码,REST framewrok在rest_framework.status
模块中提供了常用状态码常量(用于Response(status=status.HTTP_200_OK))。
信息告知 - 1xx
信息,服务器收到请求,需要请求者继续执行操作
1 | HTTP_100_CONTINUE |
成功 - 2xx
成功,操作被成功接收并处理
1 | HTTP_200_OK |
重定向 - 3xx
重定向,需要进一步的操作以完成请求
1 | HTTP_300_MULTIPLE_CHOICES |
客户端错误 - 4xx
客户端错误,请求包含语法错误或无法完成请求
1 | HTTP_400_BAD_REQUEST |
服务器错误 - 5xx
内部服务器错误
1 | HTTP_500_INTERNAL_SERVER_ERROR |
状态码含义
更多状态码含义可参考:https://seo.juziseo.com/doc/http_code/
状态码 | 状态码英文名称 | 中文描述 |
---|---|---|
100 | Continue | 继续。客户端应继续其请求 |
101 | Switching Protocols | 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议 |
200 | OK | 请求成功。一般用于GET与POST请求 |
201 | Created | 已创建。成功请求并创建了新的资源 |
202 | Accepted | 已接受。已经接受请求,但未处理完成 |
203 | Non-Authoritative Information | 非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本 |
204 | No Content | 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档 |
205 | Reset Content | 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域 |
206 | Partial Content | 部分内容。服务器成功处理了部分GET请求 |
207 | Multi-Status | 请求已成功处理,返回了多个状态的XML消息 |
300 | Multiple Choices | 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择 |
301 | Moved Permanently | 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替 |
302 | Found | 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI |
303 | See Other | 查看其它地址。与301类似。使用GET和POST请求查看 |
304 | Not Modified | 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源 |
305 | Use Proxy | 使用代理。所请求的资源必须通过代理访问 |
306 | Unused | 已经被废弃的HTTP状态码 |
307 | Temporary Redirect | 临时重定向。与302类似。使用GET请求重定向 |
400 | Bad Request | 客户端请求的语法错误,服务器无法理解 |
401 | Unauthorized | 请求要求用户的身份认证 |
402 | Payment Required | 保留,将来使用 |
403 | Forbidden | 服务器理解请求客户端的请求,但是拒绝执行此请求 |
404 | Not Found | 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置”您所请求的资源无法找到”的个性页面 |
405 | Method Not Allowed | 客户端请求中的方法被禁止 |
406 | Not Acceptable | 服务器无法根据客户端请求的内容特性完成请求 |
407 | Proxy Authentication Required | 请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权 |
408 | Request Time-out | 服务器等待客户端发送的请求时间过长,超时 |
409 | Conflict | 服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突 |
410 | Gone | 客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置 |
411 | Length Required | 服务器无法处理客户端发送的不带Content-Length的请求信息 |
412 | Precondition Failed | 客户端请求信息的先决条件错误 |
413 | Request Entity Too Large | 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息 |
414 | Request-URI Too Large | 请求的URI过长(URI通常为网址),服务器无法处理 |
415 | Unsupported Media Type | 服务器无法处理请求附带的媒体格式 |
416 | Requested range not satisfiable | 客户端请求的范围无效 |
417 | Expectation Failed | 服务器无法满足Expect的请求头信息 |
422 | Unprocessable Entity | 表示请求格式正确,但是由于含有语义错误,无法响应。 |
423 | Locked | 表示当前资源被锁定 |
424 | Failed Dependency | 表示由于之前的某个请求发生的错误,导致当前请求失败,例如PROPPATCH。 |
428 | Precondition Required | (请求未带条件)表示服务器要求请求必须带上条件。 |
429 | Too Many Requests | (并发请求过多)表示用户在一段给定的时间内发送过多的请求 |
431 | Request Header Fields Too Large | 请求头过大-表示服务器不能处理请求,因为请求的单一请求头或请求头整体过大 |
451 | Unavailable For Legal Reasons | 访问被拒绝(法律的要求)-表示(由IETF在2015核准后新增加)该访问因法律的要求而被拒绝。 |
500 | Internal Server Error | 服务器内部错误,无法完成请求 |
501 | Not Implemented | 服务器不支持请求的功能,无法完成请求 |
502 | Bad Gateway | 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应 |
503 | Service Unavailable | 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中 |
504 | Gateway Time-out | 充当网关或代理的服务器,未及时从远端服务器获取请求 |
505 | HTTP Version not supported | 服务器不支持请求的HTTP协议的版本,无法完成处理 |
507 | Insufficient Storage | 表示服务器无法存储完成请求所必须的内容。这个状况被认为是临时的 |
511 | Network Authentication Required | 表示客户端需要经过验证以获得网络连接许可 |
视图类关系
REST framework 提供了众多的通用视图基类与扩展类,以简化视图的编写。
视图类继承关系
简洁来讲drf有以下视图基类和扩展类
视图的方法与属性
视图说明
说这个之前我们先引入一个模型实例,以这个模型为例来做后面的演示。
示例模型
模型类
1 | from django.contrib.auth.models import User |
序列化类
1 | from rest_framework import serializers |
两个基类
APIView
位置:rest_framework.views.APIView
APIView
是REST framework提供的所有视图的基类,继承自Django的View
父类。
APIView
与View
的不同之处在于:
- 传入到视图方法中的是REST framework的
Request
对象,而不是Django的HttpRequeset
对象; - 视图方法可以返回REST framework的
Response
对象,视图会为响应数据设置(render)符合前端要求的格式; - 任何
APIException
异常都会被捕获到,并且处理成合适的响应信息; - 在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
支持定义的属性
- authentication_classes 列表或元祖,身份认证类
- permissoin_classes 列表或元祖,权限检查类
- throttle_classes 列表或元祖,流量控制类
在APIView
中仍以常规的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。
实例(文章增删改查)
1 | from rest_framework import status |
对应的路由
1 | path('articles/', ArticleView.as_view(), name="articles"), |
GenericAPIView
位置:rest_framework.generics.GenericAPIView
继承自APIVIew
,增加了对于列表视图和详情视图可能用到的通用支持方法。通常使用时,可搭配一个或多个Mixin扩展类。
支持定义的属性
- 列表视图与详情视图通用:
- queryset 列表视图的查询集
- serializer_class 视图使用的序列化器
- 列表视图使用:
- pagination_class 分页控制类
- filter_backends 过滤控制后端
- 详情页视图使用:
- lookup_field 查询单一数据库对象时使用的条件字段,默认为’
pk
‘ - lookup_url_kwarg 查询单一数据时URL中的参数关键字名称,默认与look_field相同
- lookup_field 查询单一数据库对象时使用的条件字段,默认为’
提供的方法
列表视图与详情视图通用:
get_queryset(self)
返回视图使用的查询集,是列表视图与详情视图获取数据的基础,默认返回
queryset
属性,可以重写,例如:1
2
3
4def get_queryset(self):
"""返回当前登录用户发表的所有文章"""
user = self.request.user
return user.articles.all()get_serializer_class(self)
返回序列化器类,默认返回
serializer_class
,可以重写,例如:1
2
3
4
5def get_serializer_class(self):
"""判断用户是否是管理员,返回不同的序列化类"""
if self.request.user.is_staff:
return FullUserSerializer
return SimpleUserSerializerget_serializer(self, *args, **kwargs)
返回序列化器对象,被其他视图或扩展类使用,如果我们在视图中想要获取序列化器对象,可以直接调用此方法。
注意,在提供序列化器对象的时候,REST framework会向对象的context属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器时使用。
详情视图使用:
get_object(self) 返回详情视图所需的模型类数据对象,默认使用
lookup_field
参数来过滤queryset。 在试图中可以调用该方法获取详情信息的模型类对象。若详情访问的模型类对象不存在,会返回404。
该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问。
示例
1 | #!/usr/bin/env python |
对应的路由:
1 | path('articles/', ArticleView.as_view(), name="articles"), |
从示例代码可以看出,使用了GenericAPIView之后,代码并没有精简,相反代码比之前更多了,只是可以将查询集和序列化器独立出来不用重复写了,但是进入到请求方法中依然要获取一下上面定义查询集和序列化器,所以GenericAPIView主要作用是配合扩展类使用的,下面我们来说说扩展类。
五个扩展类
我们已经有get, post, delete等方法了,为什么mixin类引入的方法要以list, create, retrieve, destroy方法命名呢? 这是因为请求方法不如操作名字清晰,比如get方法同时对应了获取对象列表和单个对象两种操作,使用list和retrieve方法后则很容易区分。另外post方法接受用户发过来的请求数据后,有时只需转发不需要创建模式对象实例,所以post方法不能简单等于create方法。
ListModelMixin
列表视图扩展类,提供list(request, *args, **kwargs)
方法快速实现列表视图,返回200状态码。
该Mixin的list方法会对数据进行过滤和分页。
源代码:
1 | class ListModelMixin(object): |
CreateModelMixin
创建视图扩展类,提供create(request, *args, **kwargs)
方法快速实现创建资源的视图,成功返回201状态码。
如果序列化器对前端发送的数据验证失败,返回400错误。
perform_create
这个钩子函数是CreateModelMixin
类自带的,用于执行创建对象时需要执行的其它方法,比如发送邮件等功能,有点类似于Django的信号。类似的钩子函数还有UpdateModelMixin提供的perform_update
方法和DestroyModelMixin提供的perform_destroy
方法。
数据通过了验证开始执行这个,我们之前的实例中创建文章要绑定一个作者,因为作者字段是只读的,我们可以在这个函数里手动去绑定作者。
源代码:
1 | class CreateModelMixin(object): |
RetrieveModelMixin
详情视图扩展类,提供retrieve(request, *args, **kwargs)
方法,可以快速实现返回一个存在的数据对象。
如果存在,返回200, 否则返回404。
源代码:
1 | class RetrieveModelMixin(object): |
UpdateModelMixin
更新视图扩展类,提供update(request, *args, **kwargs)
方法,可以快速实现更新一个存在的数据对象。
同时也提供partial_update(request, *args, **kwargs)
方法,可以实现局部更新。
成功返回200,序列化器校验数据失败时,返回400错误。
源代码:
1 | class UpdateModelMixin(object): |
DestroyModelMixin
删除视图扩展类,提供destroy(request, *args, **kwargs)
方法,可以快速实现删除一个存在的数据对象。
成功返回204,不存在返回404。
源代码:
1 | class DestroyModelMixin(object): |
实例
接下来的实例我们使用前面提到的GenericAPIView和扩展类组合来完成我们的实例
1 | #!/usr/bin/env python |
路由
1 | path('articles/', ArticleView.as_view(), name="articles"), |
GenericAPIView 类继承了APIView类,提供了基础的API视图。它对用户请求进行了转发,并对Django自带的request对象进行了封装。不过它比APIView类更强大,因为它还可以通过queryset
和serializer_class
属性指定需要序列化与反序列化的模型或queryset及所用到的序列化器类。
这里的 ListModelMixin
和 CreateModelMixin
类则分别引入了.list()
和.create()
方法,当用户发送get请求时调用Mixin提供的list()方法,将指定queryset序列化后输出,发送post请求时调用Mixin提供的create()方法,创建新的实例对象。
DRF还提供RetrieveModelMixin
, UpdateModelMixin
和DestroyModelMixin
类,实现了对单个对象实例的查、改和删操作
使用通用视图Generics.*类
将Mixin类和GenericAPI类混配,已经帮助我们减少了一些代码,但我们还可以做得更好,比如将get请求与mixin提供的list方法进行绑定感觉有些多余。幸好DRF还提供了一套常用的将 Mixin 类与 GenericAPI类已经组合好了的视图,开箱即用,可以进一步简化我们的代码
CreateAPIView
提供 post 方法
继承自: GenericAPIView、CreateModelMixin
ListAPIView
提供 get 方法
继承自:GenericAPIView、ListModelMixin
RetireveAPIView
提供 get 方法
继承自: GenericAPIView、RetrieveModelMixin
DestoryAPIView
提供 delete 方法
继承自:GenericAPIView、DestoryModelMixin
UpdateAPIView
提供 put 和 patch 方法
继承自:GenericAPIView、UpdateModelMixin
RetrieveUpdateAPIView
提供 get、put、patch方法
继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
RetrieveUpdateDestoryAPIView
提供 get、put、patch、delete方法
继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
实例
1 | #!/usr/bin/env python |
路由
1 | path('articles/', ArticleView.as_view(), name="articles"), |
顾名思义,generics.ListCreateAPIView
类支持List、Create两种视图功能,分别对应GET和POST请求。generics.RetrieveUpdateDestroyAPIView
支持Retrieve、Update、Destroy操作,其对应方法分别是GET、PUT和DELETE。
寥寥几行,实现了我们所有想要的功能,神不神奇?
其它常用generics类视图还包括ListAPIView
, RetrieveAPIView
, RetrieveUpdateAPIView
等等。你可以根据实际需求使用,为你的API写视图时只需要定义queryset
和serializer_class
即可。
视图集ViewSet
使用通用视图generics类后视图代码已经大大简化,但是ArticleView和ArticleDetailView
两个类中queryset
和serializer_class
属性依然存在代码重复。使用视图集可以将两个类视图进一步合并,一次性提供List、Create、Retrieve、Update、Destroy这5种常见操作,这样queryset
和seralizer_class
属性也只需定义一次就好, 这就变成了视图集(viewset)。
ViewSet
继承自APIView
,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。
在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
GenericViewSet
继承自GenericAPIView
,作用也与GenericAPIVIew类似,提供了get_object、get_queryset等方法便于列表视图与详情信息视图的开发。
ModelViewSet
继承自GenericAPIVIew
,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
ReadOnlyModelViewSet
继承自GenericAPIVIew
,同时包括了ListModelMixin、RetrieveModelMixin。
到了视图集这里,我们的目的很明确就是简化代码,以上每个视图集都注明了继承的类,从继承关系上可以看出来大概的使用方式,我们这里一步到位只讲解下代码最简化的ModelViewSet,直接上实例
实例
1 | #!/usr/bin/env python |
使用视图集后,我们需要使用DRF提供的路由router来分发urls,因为一个视图集现在对应多个urls,而不像之前的一个url对应一个视图函数或一个视图类。
1 | from django.urls import path, re_path |
你或许又要问了,一个视图集对应List、Create、Retrieve、Update、Destroy这5种操作。有时候我只需要其中的一种或几种操作,该如何实现呢?答案是在urls.py
中指定方法映射即可,如下所示:
1 |
|
另外DRF还提供了ReadOnlyModelViewSet
这个类,它仅支持list和retrive这两个可读的操作
视图集中定义附加action动作
在视图集中,除了上述默认的方法动作外,还可以添加自定义动作。
添加自定义动作需要使用rest_framework.decorators.action
装饰器。
以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同。
action装饰器可以接收两个参数:
methods: 该action支持的请求方式,列表传递
detail
: 表示是action中要处理的是否是视图资源的对象(即是否通过url路径获取主键)
- True 表示使用通过URL获取的主键对应的数据对象
- False 表示不使用URL获取主键
举例:
1 | from rest_framework import mixins |
url的定义
1 | urlpatterns = [ |
视图集继承关系
总结
本文使用了DRF提供的多种基于类的API视图的重写了文章资源API。那么这几种方式到底哪种更好呢? 答案是各有利弊。小编个人认为大家只需掌握以下三种方式即可:
- 基础的API类:可读性最高、代码最多、灵活性最高。当你需要对的API行为进行个性化定制时,建议使用这种方式。
- 通用generics类:可读性好、代码适中、灵活性较高。当你需要对一个模型进行标准的增删查改全部或部分操作时建议使用这种方式。
- 使用视图集viewset: 可读性较低、代码最少、灵活性最低。当你需要对一个模型进行标准的增删查改的全部操作且不需定制API行为时建议使用这种方式。
至于mixin类和GenericAPI的混用,这个和generics类没什么区别,大家不看也罢。
使用CBV类可以简化代码,增加重用,在很多情况下我们还需要重写父类的方法,比如get_queryset
, get_serializer_class
方法以实现特殊的功能。
Django REST Framework系列文章
- 手摸手,带你用Django REST Framework撸接口系列一(基础篇)
- 手摸手,带你用Django REST Framework撸接口系列二(序列化器篇)
- 手摸手,带你用Django REST Framework撸接口系列三(视图篇)
- 手摸手,带你用Django REST Framework撸接口系列四(渲染器篇)
- 手摸手,带你用Django REST Framework撸接口系列五(路由篇)
- 手摸手,带你用Django REST Framework撸接口系列六(认证篇)
- 手摸手,带你用Django REST Framework撸接口系列七(权限篇)
- 手摸手,带你用Django REST Framework撸接口系列八(限流篇)
- 手摸手,带你用Django REST Framework撸接口系列九(过滤篇)
- 手摸手,带你用Django REST Framework撸接口系列十(排序篇)
- 手摸手,带你用Django REST Framework撸接口系列十一(分页篇)
- 手摸手,带你用Django REST Framework撸接口系列十二(异常处理篇)
- 手摸手,带你用Django REST Framework撸接口系列十三(自动生成接口文档篇)