drf笔记
Django-Rest-Framework
RESTful规范

1.HTTPS协议
建议使用https协议替代http协议,让接口数据更加安全。
这一条其实与开发无关,在最后的项目部署时只要使用https部署即可。

如果是基于HTTP协议,则意味着用户浏览器再向服务器发送数据时,都是以明文的形式传输,如果你在某咖啡厅上网,他就可以把你网络传输的数据明文都获取到。
如果是基于HTTPS协议,则意味着用户浏览器再向服务器发送数据时,都是以密文的形式传输,中途即使有非法用户获取到网络数据,也可以密文的,无法破译。
HTTPS保证了数据安全,但由数据传输存在着加密和解密的过程,所以会比HTTP协议慢一些。
注意:此处大家先了解https和http的不同,至于底层原理和部署,在项目部署时再细讲。
参考文档:https://www.cnblogs.com/wupeiqi/p/11647089.html
2. 域名
对于后端API接口中要体现API标识,例如:
- https://api.example.com
- https://www.example.com/api/


3. 版本
对于后端API接口中要体现版本,例如:
- http://api.example.com/v1/
- http://api.example.com/?version=v1
- http://v1.example.com/
- http://api.example.com/
请求头:Accept: application/json; version=v1

4. 路径
restful API这种风格中认为网络上的一切都称是资源,围绕着资源可以进行 增删改查等操作。
这些资源,在URL中要使用名词表示(可复数),围绕着资源进行的操作就用Method不同进行区分。
https://api.example.com/v1/person
https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees
5. 请求方法
根据请求方法不同进行不同的操作。
GET 在服务器取出资源(一项或多项)
POST 在服务器新建一个资源
PUT 在服务器更新资源(客户端提供改变后的完整资源)
PATCH 在服务器更新资源(客户端提供改变的属性)
DELETE 在服务器删除资源
例如:
https://api.example.com/v1/users
https://api.example.com/v1/users/1/
接口:/users/ 方法:GET => 用户列表
接口:/users/ 方法:POST => 添加用户
接口:/users/(\d+)/ 方法:GET => 获取单条数据
接口:/users/(\d+)/ 方法:DELETE => 删除数据
接口:/users/(\d+)/ 方法:PUT => 更新数据
接口:/users/(\d+)/ 方法:PATCH => 局部更新
6. 搜索条件
在URL中通过参数的形式来传递搜索条件。
https://api.example.com/v1/users
https://api.example.com/v1/zoos?limit=10 指定返回记录的数量
https://api.example.com/v1/zoos?offset=10 指定返回记录的开始位置
https://api.example.com/v1/zoos?page=2&per_page=100 指定第几页,以及每页的记录数
https://api.example.com/v1/zoos?sortby=name&order=asc 指定返回结果按照哪个属性排序,以及排序顺序
https://api.example.com/v1/zoos?animal_type_id=1 指定筛选条件
7. 返回数据
针对不同操作,服务器向用户返回的结果结构应该不同。
https://api.example.com/v1/users
https://api.example.com/v1/users/2/
| URL | 方法 | 描述 | 返回数据 |
|---|---|---|---|
| /users/ | GET | 列表 | 返回资源对象的列表 [ {id:1,name:"武沛齐"}, {id:1,name:"日天"} ] |
| /users/ | POST | 添加 | 返回新生成的资源对象 |
| /users/(\d+)/ | GET | 获取单条数据 | 返回单个资源对象 |
| /users/(\d+)/ | DELETE | 删除数据 | 返回一个空文档 null |
| /users/(\d+)/ | PUT | 更新数据 | 返回完整的资源对象 |
| /users/(\d+)/ | PATCH | 局部更新 | 返回完整的资源对象 |
一般在实际的开发过程中会对上述返回数据进行补充和完善,例如:每次请求都返回一个字典,其中包含:
- code,表示返回码,用于表示请求执行请求,例如:0表示请求成功,1003表示参数非法,40009数据量太大等。
- data,表示数据
- error,错误信息
{
code:0,
data:[ {id:1,name:"武沛齐"}, {id:1,name:"日天"} ]
}
{
code:41007,
error:"xxxxx"
}
8. 状态码
后端API在对请求进行响应时,除了返回数据意外还可以返回状态码,来表示请求状况。
from django.urls import path, include
from app01 import views
urlpatterns = [
path('users/', views.users),
]
from django.http import JsonResponse
def users(request):
info = {
"code": 1000,
"data": {"id":1,"name":"武沛齐"}
}
return JsonResponse(info, status=200)

200 OK - [GET]:服务器成功返回用户请求的数据
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作。
401 Unauthorized - [*]:表示用户未认证(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
状态码可以表示一部分的服务端的处理请求,但特别细致的信息无法全都都包括,所以一般在开发中会配合code返回码来进行。
例如:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Global_Return_Code.html
9. 错误处理
错误处理,状态码是4xx时,应返回错误信息,error当做key。
{
error: "Invalid API key"
}
了解常用resful规范,那么以后在开发后端API接口时,就要根据上述要求遵循restful规范(非必须,视公司情况灵活变通)。
案例展示
基于django + restful规范来开发一个后台接口示例。
# urls.py
from django.urls import path
from app01 import views
# http://www.xxx.com/api/v1/users/
urlpatterns = [
path('api/<str:version>/users/', views.users),
path('api/<str:version>/users/<int:pk>/', views.users),
]
# views.py
from django.http import JsonResponse
def users(request, version, pk=None):
print("版本:", version)
if not pk:
if request.method == "GET":
# 请求用户列表
info = {
"code": 0,
"data": [{"id": 1, "name": "武沛齐"}]
}
return JsonResponse(info)
elif request.method == "POST":
# 新增用户,读取 request.POST 中提交的数据并添加到数据库中
info = {
"code": 0,
"data": {"id": 1, "name": "武沛齐"}
}
return JsonResponse(info)
else:
info = {
"code": 1000,
"error": "请求错误"
}
return JsonResponse(info)
if request.method == "GET":
# 获取ID=pk的用户信息,并返回
info = {
"code": 0,
"data": {"id": 1, "name": "武沛齐"}
}
return JsonResponse(info)
elif request.method == "DELETE":
# 删除id=pk的用户
info = {
"code": 0,
"data": {}
}
return JsonResponse(info)
elif request.method == "PUT":
# 读取request.POST中的数据 + pk,更新数据库中的用户信息
info = {
"code": 0,
"data": {"id": 1, "name": "武沛齐"}
}
return JsonResponse(info)
elif request.method == "PATCH":
# 读取request.POST中的数据 + pk,更新数据库中的用户信息
info = {
"code": 0,
"data": {"id": 1, "name": "武沛齐"}
}
return JsonResponse(info)
else:
info = {
"code": 1000,
"error": "请求错误"
}
return JsonResponse(info)
Django的FBV和CBV
基于django开发项目时,对于视图可以使用 FBV 和 CBV 两种模式编写。
-
FBV,function base views,其实就是编写函数来处理业务请求。
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('users/', views.users), ]from django.http import JsonResponse def users(request,*args, **kwargs): if request.method == "GET": return JsonResponse({"code":1000,"data":"xxx"}) elif request.method == 'POST': return JsonResponse({"code":1000,"data":"xxx"}) ... -
CBV,class base views,其实就是编写类来处理业务请求。
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('users/', views.UserView.as_view()), ]from django.views import View class UserView(View): def get(self, request, *args, **kwargs): return JsonResponse({"code": 1000, "data": "xxx"}) def post(self, request, *args, **kwargs): return JsonResponse({"code": 1000, "data": "xxx"})
其实,CBV和FBV的底层实现本质上相同的。

CBV,其实就是在FBV的基础上进行的功能的扩展,根据请求的方式不同,直接定位到不同的函数中去执行。
如果是基于django编写restful API,很显然使用CBV的方式会更加简洁,因为restful规范中就是根据method不同来执行不同操作。
基于django的CBV和restful规范开发实战案例:
# urls.py
from django.urls import path
from app01 import views
urlpatterns = [
# http://www.xxx.com/api/v1/users/
path('api/<str:version>/users/', views.UserView.as_view()),
# http://www.xxx.com/api/v1/users/2/
path('api/<str:version>/users/<int:pk>/', views.UserView.as_view()),
]
# views.py
from django.views import View
from django.http import JsonResponse
class UserView(View):
def get(self, request, version, pk=None):
if not pk:
# 请求用户列表
info = {
"code": 0,
"data": [
{"id": 1, "name": "武沛齐"},
{"id": 1, "name": "武沛齐"},
]
}
return JsonResponse(info)
else:
# 获取ID=pk的用户信息,并返回
info = {
"code": 0,
"data": {"id": 1, "name": "武沛齐"}
}
return JsonResponse(info)
def post(self, request, version):
# 新增用户,读取 request.POST 中提交的数据并添加到数据库中
info = {
"code": 0,
"data": {"id": 1, "name": "武沛齐"}
}
return JsonResponse(info)
def delete(self, request, version, pk):
# 删除id=pk的用户
info = {
"code": 0,
"data": {}
}
return JsonResponse(info)
def put(self, request, version, pk):
# 读取request.POST中的数据 + pk,更新数据库中的用户信息
info = {
"code": 0,
"data": {"id": 1, "name": "武沛齐"}
}
return JsonResponse(info)
def patch(self, request, version, pk):
# 读取request.POST中的数据 + pk,更新数据库中的用户信息
info = {
"code": 0,
"data": {"id": 1, "name": "武沛齐"}
}
return JsonResponse(info)
从上面的示例看来,基于django框架完全可以开发restful API。
django restframework框架 是在django的基础上又给我们提供了很多方便的功能,让我们可以更便捷基于django开发restful API,来一个简单的实例,快速了解下:
-
基于django

-
基于django + django restframework框架

django restframework
django restframework(简称drf)本质上其实就是一个别人编写好的app,里面集成了很多编写restful API的功能功能,接下里咱们就来学习drf并用他来开发restful API。
drf内置了很多便捷的功能,在接下来的课程中会给大家依次讲解下面的内容:
- 快速上手
- 请求的封装
- 版本管理
- 认证
- 权限
- 限流
- 序列化
- 视图
- 条件搜索
- 分页
- 路由
- 解析器
1. 快速上手
-
安装
pip install djangorestframework==3.12.4版本要求:djangorestframework==3.12.4 Python (3.5, 3.6, 3.7, 3.8, 3.9) Django (2.2, 3.0, 3.1) 版本要求:djangorestframework==3.11.2 Python (3.5, 3.6, 3.7, 3.8) Django (1.11, 2.0, 2.1, 2.2, 3.0) -
配置,在settings.py中添加配置
INSTALLED_APPS = [ ... # 注册rest_framework(drf) 'rest_framework', ] # drf相关配置以后编写在这里 REST_FRAMEWORK = { } -
URL和视图
# urls.py from django.urls import path from app01 import views urlpatterns = [ path('users/', views.UserView.as_view()), ]# views.py from rest_framework.views import APIView from rest_framework.response import Response class UserView(APIView): def get(self, request, *args, **kwargs): return Response({"code": 1000, "data": "xxx"}) def post(self, request, *args, **kwargs): return Response({"code": 1000, "data": "xxx"})
其实drf框架是在django基础进行的扩展,所以上述执行过得底层实现流程(同django的CBV):

drf中重写了 as_view 和dispatch方法,其实就是在原来django的功能基础上添加了一些功能,例如:
as_view,免除了csrf 验证,一般前后端分离不会使用csrf token认证(后期会使用jwt认证)。dispatch,内部添加了 版本处理、认证、权限、访问频率限制等诸多功能(后期逐一讲解)。
2. 请求数据的封装
以前我们通过django开发项目时,视图中的request是 django.core.handlers.wsgi.WSGIRequest 类的对象,其中包含了请求相关的所有数据。
# Django FBV
def index(request):
request.method
request.POST
request.GET
request.body
# Django CBV
from django.views import View
class UserView(View):
def get(self,request):
request.method
request.POST
request.GET
request.body
而在使用drf框架时,视图中的request是rest_framework.request.Request类的对象,其是又对django的request进行了一次封装,包含了除django原request对象以外,还包含其他后期会使用的其他对象。
from rest_framework.views import APIView
from rest_framework.response import Response
class UserView(APIView):
def get(self, request, *args, **kwargs):
# request,不再是django中的request,而是又被封装了一层,内部包含:django的request、认证、解析器等。
return Response({"code": 1000, "data": "xxx"})
def post(self, request, *args, **kwargs):
return Response({"code": 1000, "data": "xxx"})
对象 = (request, 其他数据)
# rest_framework.request.Request 类
class Request:
"""
Wrapper allowing to enhance a standard `HttpRequest` instance.
Kwargs:
- request(HttpRequest). The original request instance. (django中的request)
- parsers(list/tuple). The parsers to use for parsing the
request content.
- authenticators(list/tuple). The authenticators used to try
authenticating the request's user.
"""
def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None):
self._request = request
self.parsers = parsers or ()
self.authenticators = authenticators or ()
...
@property
def query_params(self):
"""
More semantically correct name for request.GET.
"""
return self._request.GET
@property
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data
def __getattr__(self, attr): # 如果方法可以通过反射找到,就直接执行request._request.attr
try:
return getattr(self._request, attr) # self._request.method
except AttributeError:
return self.__getattribute__(attr)
所以,在使用drf框架开发时,视图中的request对象与原来的有些不同,例如:
from rest_framework.views import APIView
from rest_framework.response import Response
from django.views import View
from rest_framework.request import Request
class UserView(APIView):
def get(self, request, *args, **kwargs):
# 通过对象的嵌套直接找到原request,读取相关值
request._request.method
request._request.GET
request._request.POST
request._request.body
# 举例:
content-type: url-form-encoded
v1=123&v2=456&v3=999
django一旦读取到这个请求头之后,就会按照 {"v1":123,"v2":456,"v3":999}
content-type: application/json
{"v1":123,"v2":456}
request._request.POST
request._request.body
# 直接读取新request对象中的值,一般此处会对原始的数据进行一些处理,方便开发者在视图中使用。
request.query_params # 内部本质上就是 request._request.GET
request.data # 内部读取请求体中的数据,并进行处理,例如:请求者发来JSON格式,他的内部会对json字符串进行反序列化。
# 通过 __getattr__ 去访问 request._request 中的值
request.method
底层源码实现:

3. 版本管理
在restful规范中要去,后端的API中需要体现版本。
drf框架中支持5种版本的设置。
3.1 URL的GET参数传递

# settings.py
REST_FRAMEWORK = {
"VERSION_PARAM": "v", # 配置version 名
"DEFAULT_VERSION": "v1",
"ALLOWED_VERSIONS": ["v1", "v2", "v3"],
"DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.QueryParameterVersioning" # 默认使用版本类
}
源码执行流程:

3.2 URL路径传递

# views.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning,URLPathVersioning
# Create your views here.
class UserView(APIView):
versioning_class = URLPathVersioning
def get(self, request, *args, **kwargs):
print(request.version)
return Response({"code": 1000, "data": "xxx"})
def post(self, request, *args, **kwargs):
return Response({"code": 1000, "data": "xxx"})
# urls.py
from django.contrib import admin
from django.urls import path,re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('api/<str:version>/users/',views.UserView.as_view()),
# re_path(r'api/(?P<version>\w+)/users/',views.UserView.as_view())
# settings.py
REST_FRAMEWORK = {
"VERSION_PARAM": "v", # 配置version 名
"DEFAULT_VERSION": "v1",
"ALLOWED_VERSIONS": ["v1", "v2", "v3"],
"DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning"
}
3.3 请求头传递

3.4 二级域名传递

在使用二级域名这种模式时需要先做两个配置:
-
域名需解析至IP,本地可以在hosts文件中添加

127.0.0.1 v1.wupeiqi.com 127.0.0.1 v2.wupeiqi.com -
在django的settings.py配置文件中添加允许域名访问
ALLOWED_HOSTS = ["*"]
3.5 路由的namespace传递

以上就是drf中支持的5种版本管理的类的使用和配置。
全局配置
上述示例中,如果想要应用某种 版本 的形式,需要在每个视图类中定义类变量:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning
class UserView(APIView):
versioning_class = QueryParameterVersioning
...
如果你项目比较大,需要些很多的视图类,在每一个类中都写一遍会比较麻烦,所有drf中也支持了全局配置。
# settings.py
REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning", # 处理版本的类的路径
"VERSION_PARAM": "version", # URL参数传参时的key,例如:xxxx?version=v1
"ALLOWED_VERSIONS": ["v1", "v2", "v3"], # 限制支持的版本,None表示无限制
"DEFAULT_VERSION": "v1", # 默认版本
}

访问URL:
http://127.0.0.1:8000/api/users/?version=v1
http://127.0.0.1:8000/api/users/?version=v2
http://127.0.0.1:8000/api/users/?version=v3
http://127.0.0.1:8000/api/admin/?version=v1
http://127.0.0.1:8000/api/admin/?version=v2
http://127.0.0.1:8000/api/admin/?version=v3
http://127.0.0.1:8000/api/v1/order/
http://127.0.0.1:8000/api/v2/order/
http://127.0.0.1:8000/api/v3/order/
底层源码实现

反向生成URL
在每个版本处理的类中还定义了reverse方法,他是用来反向生成URL并携带相关的的版本信息用的,例如:


小结
以后使用drf开发后端API接口时:
- 创建django程序
- 安装drf框架
- 创建一个app专门来处理用户的请求
- 注册APP
- 设置版本
- 编写视图类
4. 认证

在开发后端的API时,不同的功能会有不同的限制,例如:
- 无需认证,就可以访问并获取数据。
- 需认证,用户需先登录,后续发送请求需携带登录时发放的凭证(后期会讲jwt)
在drf中也给我们提供了 认证组件 ,帮助我们快速实现认证相关的功能,例如:
# models.py
from django.db import models
class UserInfo(models.Model):
username = models.CharField(verbose_name="用户名", max_length=32)
password = models.CharField(verbose_name="密码", max_length=64)
token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)


import uuid
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from app01 import models
# Create your views here.
class AuthView(APIView):
""" 用户登录认证 """
def post(self, request, *args, **kwargs):
print(request.data) # {"username": "admin", "password": "123"}
username = request.data.get('username')
password = request.data.get('password')
user_object = models.UserInfo.objects.filter(username=username,password=password).first()
if not user_object:
return Response({"code": 1000, "data": "用户名或密码错误"})
token = str(uuid.uuid4())
user_object.token = token
user_object.save()
# 登录成功返回值
return Response({"code": 0, "data": {"token": token, "name": username}})
class TokenAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.query_params.get("token")
if not token:
raise AuthenticationFailed({"code": 1002, "data": "认证失败"})
user_object = models.UserInfo.objects.filter(token=token).first()
if not user_object:
raise AuthenticationFailed({"code": 1002, "data": "认证失败"})
return user_object, token
def authenticate_header(self, request):
return 'Bearer realm="API"'
class OrderView(APIView):
authentication_classes = [TokenAuthentication, ]
def get(self, request, *args, **kwargs):
print(request.user)
return Response({"code": 0, "data": {"user": None, 'list': [1, 2, 3]}})
class PayView(APIView):
authentication_classes = [TokenAuthentication, ]
def get(self, request, *args, **kwargs):
print(request.user)
return Response({"code": 0, "data": "数据..."})
在视图类中设置类变量 authentication_classes的值为 认证类 MyAuthentication,表示此视图在执行内部功能之前需要先经过 认证。
认证类的内部就是去执行:authenticate方法,根据返回值来表示认证结果。
-
抛出异常AuthenticationFailed,表示认证失败。内部还会执行
authenticate_header将返回值设置给响应头WWW-Authenticate -
返回含有两个元素的元组,表示认证成功,并且会将元素的第1个元素赋值给
request.user、第2个值赋值给request.auth。第1个值,一般是用户对象。 第2个值,一般是token -
返回None,表示继续调用 后续的认证类 进行认证(上述案例未涉及)
**关于 ”返回None“ **
接下来说说 “返回None” 是咋回事。
在视图类的
authentication_classes中定义认证类时,传入的是一个列表,支持定义多个认证类。当出现多个认证类时,drf内部会按照列表的顺序,逐一执行认证类的
authenticate方法,如果 返回元组 或 抛出异常 则会终止后续认证类的执行;如果返回None,则意味着继续执行后续的认证类。class TokenAuthentication(BaseAuthentication): def authenticate(self, request): # 跳过当前认证类,继续执行下一个认证类 return None token = request.query_params.get("token") if not token: raise AuthenticationFailed({"code": 1002, "data": "认证失败"}) user_object = models.UserInfo.objects.filter(token=token).first() if not user_object: raise AuthenticationFailed({"code": 1002, "data": "认证失败"}) return user_object, token def authenticate_header(self, request): return 'Bearer realm="API"'如果所有的认证类
authenticate都返回了None,则默认 request.user="AnonymousUser" 和 request.auth=None,也可以通过修改配置文件来修改默认值。REST_FRAMEWORK = { "UNAUTHENTICATED_USER": lambda: None, "UNAUTHENTICATED_TOKEN": lambda: None, }
”返回None“的应用场景:
当某个API,已认证 和 未认证 的用户都可以方法时,比如:
- 已认证用户,访问API返回该用户的视频播放记录列表。
- 未认证用户,访问API返回最新的的视频列表。
注意:不同于之前的案例,之前案例是:必须认证成功后才能访问,而此案例则是已认证和未认证均可访问。

关于多个认证类
一般情况下,编写一个认证类足矣。
当项目中可能存在多种认证方式时,就可以写多个认证类。例如,项目认证支持:
- 在请求中传递token进行验证。
- 请求携带cookie进行验证。
- 请求携带jwt进行验证(后期讲)。
- 请求携带的加密的数据,需用特定算法解密(一般为app开发的接口都是有加密算法)
- ...
此时,就可以编写多个认证类,并按照需要应用在相应的视图中,例如:

注意:此示例后续在视图中读取的 request.user 的值为None时,表示未认证成功;不为None时,则表示认证成功。
全局配置
在每个视图类的类变量 authentication_classes 中可以定义,其实在配置文件中也可以进行全局配置,例如:
REST_FRAMEWORK = {
"UNAUTHENTICATED_USER": lambda: None,
"UNAUTHENTICATED_TOKEN": lambda: None,
"DEFAULT_AUTHENTICATION_CLASSES":["xxxx.xxxx.xx.类名","xxxx.xxxx.xx.类名",]
}
底层源码实现:

5. 权限
认证,根据用户携带的 token/其他 获取当前用户信息。
权限,读取认证中获取的用户信息,判断当前用户是否有权限访问,例如:普通用户、管理员、超级用户,不同用户具有不同的权限。
class UserInfo(models.Model):
role_choices = ((1, "普通用户"), (2, "管理员"), (3, "超级管理员"),)
role = models.IntegerField(verbose_name="角色", choices=role_choices, default=1)
username = models.CharField(verbose_name="用户名", max_length=32)
password = models.CharField(verbose_name="密码", max_length=64)
token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)

import uuid
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.permissions import BasePermission
from rest_framework.exceptions import AuthenticationFailed
from app01 import models
class AuthView(APIView):
""" 用户登录认证 """
authentication_classes = []
permission_classes = []
def post(self, request, *args, **kwargs):
print(request.data) # {"username": "wupeiqi", "password": "123"}
username = request.data.get('username')
password = request.data.get('password')
user_object = models.UserInfo.objects.filter(username=username, password=password).first()
if not user_object:
return Response({"code": 1000, "data": "用户名或密码错误"})
token = str(uuid.uuid4())
user_object.token = token
user_object.save()
return Response({"code": 0, "data": {"token": token, "name": username}})
class TokenAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.query_params.get("token")
if not token:
raise AuthenticationFailed({"code": 1002, "data": "认证失败"})
user_object = models.UserInfo.objects.filter(token=token).first()
if not user_object:
raise AuthenticationFailed({"code": 1002, "data": "认证失败"})
return user_object, token
def authenticate_header(self, request):
return 'Bearer realm="API"'
class PermissionA(BasePermission):
# 权限失败的错误信息
message = {"code": 1003, 'data': "无权访问"}
def has_permission(self, request, view):
if request.user.role == 2:
return True
return False
# 暂时先这么写
def has_object_permission(self, request, view, obj):
return True
class OrderView(APIView):
authentication_classes = [TokenAuthentication, ]
permission_classes = [PermissionA,]
def get(self, request, *args, **kwargs):
print(request.user)
return Response({"code": 0, "data": {"user": None, 'list': [1, 2, 3]}})
class PayView(APIView):
authentication_classes = [TokenAuthentication, ]
permission_classes = [PermissionA, ]
def get(self, request, *args, **kwargs):
print(request.user)
return Response({"code": 0, "data": "数据..."})
关于多个权限类
当开发过程中需要用户同时具备多个权限(缺一不可)时,可以用多个权限类来实现。
权限组件内部处理机制:按照列表的顺序逐一执行 has_permission 方法,如果返回True,则继续执行后续的权限类;如果返回None或False,则抛出权限异常并停止后续权限类的执行。
# models.py
from django.db import models
class Role(models.Model):
""" 角色表 """
title = models.CharField(verbose_name="名称", max_length=32)
class UserInfo(models.Model):
""" 用户表 """
username = models.CharField(verbose_name="用户名", max_length=32)
password = models.CharField(verbose_name="密码", max_length=64)
token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)
roles = models.ManyToManyField(verbose_name="角色", to="Role")

# urls.py
from django.urls import path, re_path, include
from app01 import views
urlpatterns = [
path('api/auth/', views.AuthView.as_view()),
path('api/order/', views.OrderView.as_view()),
]
# views.py
import uuid
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.permissions import BasePermission
from rest_framework.exceptions import AuthenticationFailed
from app01 import models
class AuthView(APIView):
""" 用户登录认证 """
def post(self, request, *args, **kwargs):
print(request.data) # {"username": "wupeiqi", "password": "123"}
username = request.data.get('username')
password = request.data.get('password')
user_object = models.UserInfo.objects.filter(username=username, password=password).first()
if not user_object:
return Response({"code": 1000, "data": "用户名或密码错误"})
token = str(uuid.uuid4())
user_object.token = token
user_object.save()
return Response({"code": 0, "data": {"token": token, "name": username}})
class TokenAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.query_params.get("token")
if not token:
raise AuthenticationFailed({"code": 1002, "data": "认证失败"})
user_object = models.UserInfo.objects.filter(token=token).first()
if not user_object:
raise AuthenticationFailed({"code": 1002, "data": "认证失败"})
return user_object, token
def authenticate_header(self, request):
return 'Bearer realm="API"'
class PermissionA(BasePermission):
message = {"code": 1003, 'data': "无权访问"}
def has_permission(self, request, view):
exists = request.user.roles.filter(title="员工").exists()
if exists:
return True
return False
def has_object_permission(self, request, view, obj):
return True
class PermissionB(BasePermission):
message = {"code": 1003, 'data': "无权访问"}
def has_permission(self, request, view):
exists = request.user.roles.filter(title="主管").exists()
if exists:
return True
return False
def has_object_permission(self, request, view, obj):
return True
class OrderView(APIView):
authentication_classes = [TokenAuthentication, ]
permission_classes = [PermissionA, PermissionA]
def get(self, request, *args, **kwargs):
return Response({"code": 0, "data": {"user": None, 'list': [1, 2, 3]}})
class PayView(APIView):
authentication_classes = [TokenAuthentication, ]
permission_classes = [PermissionA, ]
def get(self, request, *args, **kwargs):
return Response({"code": 0, "data": "数据..."})
关于 has_object_permission
当我们使用drf来编写 视图类时,如果是继承 APIView,则 has_object_permission不会被执行(没用),例如:
from rest_framework.views import APIView
class PayView(APIView):
authentication_classes = [TokenAuthentication, ]
permission_classes = [PermissionA, ]
def get(self, request, *args, **kwargs):
return Response({"code": 0, "data": "数据..."})
但是,当我们后期学习了 视图类的各种骚操作之后,发现视图也可以继承 GenericAPIView,此时 有可能 会执行 has_object_permission 用于判断是否有权限访问某个特定ID的对象。

调用 self.get_object 方法时,会按照 permission_classes中权限组件的顺序,依次执行他们的 has_object_permission 方法。
self.get_object其实就根据用户传入的 pk,搜索并获取某个对象的过程。
全局配置
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES":["xxxx.xxxx.xx.类名","xxxx.xxxx.xx.类名",]
}
权限底层源码实现

小结
- 请求的封装
- 版本的处理
- 过程:选择版本处理类,获取用户传入的版本信息
- 结果:在
request.version = 版本、request.versioning_scheme=版本处理类的对象
- 认证组件,在视图执行之前判断用户是否认证成功。
- 过程:执行所有的认证类中的
authenticate方法- 返回None,继续执行后续的认证类(都未认证成功,request.user 和 auth有默认值,也可以全局配置)
- 返回2个元素的元组,中断
- 抛出
AuthenticationFailed,中断
- 结果:在
request.user和request.auth赋值(后续代码可以使用)
- 过程:执行所有的认证类中的
- 权限
- 过程:执行所有的权限类的
has_permission方法,只有所有都返回True时,才表示具有权限 - 结果:有权限则可以执行后续的视图,无权限则直接返回 自定义的错误信息
- 过程:执行所有的权限类的
6. 限流
限流,限制用户访问频率,例如:用户1分钟最多访问100次 或者 短信验证码一天每天可以发送50次, 防止盗刷。
- 对于匿名用户,使用用户IP作为唯一标识。
- 对于登录用户,使用用户ID或名称作为唯一标识。
缓存={
用户标识:[12:33,12:32,12:31,12:30,12,] 1小时/5次 12:34 11:34
{
pip3 install django-redis
# settings.py
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "qwe123",
}
}
}

CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "qwe123",
}
}
}
from django.urls import path, re_path
from app01 import views
urlpatterns = [
path('api/order/', views.OrderView.as_view()),
]
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import exceptions
from rest_framework import status
from rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache as default_cache
class ThrottledException(exceptions.APIException):
status_code = status.HTTP_429_TOO_MANY_REQUESTS
default_code = 'throttled'
class MyRateThrottle(SimpleRateThrottle):
cache = default_cache # 访问记录存放在django的缓存中(需设置缓存)
scope = "user" # 构造缓存中的key
cache_format = 'throttle_%(scope)s_%(ident)s'
# 设置访问频率,例如:1分钟允许访问10次
# 其他:'s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day'
THROTTLE_RATES = {"user": "10/m"}
def get_cache_key(self, request, view):
"""返回用户唯一表示,用来记录表示用户信息"""
if request.user:# 已经登录的用用户信息做标识
ident = request.user.pk # 用户ID
else: # 未登录的用 用户的ip做标识
ident = self.get_ident(request) # 获取请求用户IP(去request中找请求头)
# throttle_u # throttle_user_11.11.11.11ser_2
return self.cache_format % {'scope': self.scope, 'ident': ident}
def throttle_failure(self):
wait = self.wait()
detail = {
"code": 1005,
"data": "访问频率限制",
'detail': "需等待{}s才能访问".format(int(wait))
}
raise ThrottledException(detail)
class OrderView(APIView):
throttle_classes = [MyRateThrottle, ]
def get(self, request):
return Response({"code": 0, "data": "数据..."})
多个限流类
本质,每个限流的类中都有一个 allow_request 方法,此方法内部可以有三种情况:
- 返回True,表示当前限流类允许访问,继续执行后续的限流类。
- 返回False,表示当前限流类不允许访问,继续执行后续的限流类。所有的限流类执行完毕后,读取所有不允许的限流,并计算还需等待的时间。
- 抛出异常,表示当前限流类不允许访问,后续限流类不再执行。
全局配置
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES":["xxx.xxx.xx.限流类", ],
"DEFAULT_THROTTLE_RATES": {
"user": "10/m",
"xx":"100/h"
}
}
底层源码实现:


# settings.py
...
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "qwe123",
}
}
}
# urls.py
from django.urls import path, re_path, include
from app01 import views
urlpatterns = [
path('api/order/', views.OrderView.as_view()),
]
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import exceptions
from rest_framework import status
from rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache as default_cache
class ThrottledException(exceptions.APIException):
status_code = status.HTTP_429_TOO_MANY_REQUESTS
default_code = 'throttled'
class MyRateThrottle(SimpleRateThrottle):
cache = default_cache # 访问记录存放在django的缓存中(需设置缓存)
scope = "user" # 构造缓存中的key
cache_format = 'throttle_%(scope)s_%(ident)s'
# 设置访问频率,例如:1分钟允许访问10次
# 其他:'s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day'
THROTTLE_RATES = {"user": "10/m"}
def get_cache_key(self, request, view):
if request.user:
ident = request.user.pk # 用户ID
else:
ident = self.get_ident(request) # 获取用户IP
return self.cache_format % {'scope': self.scope, 'ident': ident}
class OrderView(APIView):
throttle_classes = [MyRateThrottle, ]
def get(self, request):
return Response({"code": 0, "data": "数据..."})
def throttled(self, request, wait):
# 如果有多个限流类,可以在视图类里做限流判断,然后抛出异常,在限流类里可以不抛出异常
detail = {
"code": 1005,
"data": "访问频率",
'detail': "需等待{}s才能访问".format(int(wait))
}
raise ThrottledException(detail)
7. Serializer(*)
drf中为我们提供了Serializer,他主要有两大功能:
- 对请求数据校验(底层调用Django的Form和ModelForm)
- 对数据库查询到的对象进行序列化
7.1.数据校验
示例1:基于Serializer:

# urls.py
path('api/users/',views.UserView.as_view()),
# models.py
class UserInfo(models.Model):
""" 用户表 """
level_choices = ((1, "普通会员"), (2, "VIP"), (3, "SVIP"),)
level = models.IntegerField(verbose_name="级别", choices=level_choices, default=1)
username = models.CharField(verbose_name="用户名", max_length=32)
password = models.CharField(verbose_name="密码", max_length=64)
age = models.IntegerField(verbose_name="年龄", default=0)
email = models.CharField(verbose_name="邮箱", max_length=64)
token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)
# views.py
import re
from django.core.validators import EmailValidator
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import exceptions
from app01 import models
class RegexValidator:
def __init__(self, base):
self.base = str(base)
def __call__(self, value):
match_object = re.match(self.base, value)
if not match_object:
raise serializers.ValidationError('格式错误')
class UserSerializer(serializers.Serializer):
username = serializers.CharField(label='姓名', min_length=6, max_length=32)
age = serializers.IntegerField(label='年龄', min_value=0, max_value=200)
level = serializers.ChoiceField(label='级别', choices=models.UserInfo.level_choices)
email = serializers.CharField(label='邮箱', validators=[EmailValidator, ])
email1 = serializers.EmailField(label='邮箱1')
email2 = serializers.CharField(label='邮箱2', validators=[RegexValidator(r"^\w+@\w+\.\w+$"), ])
email3 = serializers.CharField(label='邮箱3')
def validate_email3(self, value):
"""钩子函数,用于验证某个字段"""
if re.match(r"^\w+@\w+\.\w+$", value):
return value
raise exceptions.ValidationError('邮箱格式校验错误')
class UserView(APIView):
"""用户管理"""
def post(self, request):
"""添加用户"""
ser = UserSerializer(data=request.data, )
if not ser.is_valid():
return Response({'code': 1006, "data": ser.errors})
print(ser.validated_data)
return Response({'code': 0, 'data': '创建成功'})
示例2:基于ModelSerializer,类似于modelform:
# models.py
from django.db import models
class Role(models.Model):
""" 角色表 """
title = models.CharField(verbose_name="名称", max_length=32)
class Department(models.Model):
""" 部门表 """
title = models.CharField(verbose_name="名称", max_length=32)
class UserInfo(models.Model):
""" 用户表 """
level_choices = ((1, "普通会员"), (2, "VIP"), (3, "SVIP"),)
level = models.IntegerField(verbose_name="级别", choices=level_choices, default=1)
username = models.CharField(verbose_name="用户名", max_length=32)
password = models.CharField(verbose_name="密码", max_length=64)
age = models.IntegerField(verbose_name="年龄", default=0)
email = models.CharField(verbose_name="邮箱", max_length=64)
token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)
# 外键
depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE)
# 多对多
roles = models.ManyToManyField(verbose_name="角色", to="Role")
# views.py
import re
from django.core.validators import EmailValidator
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import exceptions
from app01 import models
class RegexValidator:
def __init__(self, base):
self.base = str(base)
def __call__(self, value):
match_object = re.match(self.base, value)
if not match_object:
raise serializers.ValidationError('格式错误')
class UserSerializer(serializers.ModelSerializer):
email2 = serializers.CharField(label='邮箱2', validators=[RegexValidator(r"^\w+@\w+\.\w+$"), ])
email3 = serializers.CharField(label='邮箱3')
class Meta:
model = models.UserInfo
fields = ['username', 'age', 'email', 'email2', 'email3']
extra_kwargs = {
'username': {'min_length': 6, 'max_length': 32},
'email': {'validators': [EmailValidator, ]}
}
def validate_email3(self, value):
"""钩子函数,用于验证某个字段"""
if re.match(r"^\w+@\w+\.\w+$", value):
return value
raise exceptions.ValidationError('邮箱格式校验错误')
class UserView(APIView):
"""用户管理"""
def post(self, request):
"""添加用户"""
ser = UserSerializer(data=request.data)
if not ser.is_valid():
return Response({'code': 1006, "data": ser.errors})
ser.validated_data.pop('email2')
ser.validated_data.pop('email3')
ls = ser.save(level=1,password="123",depart_id=1)
print(ls)
return Response({'code': 0, 'data': '创建成功'})

提示:save方法会返回新生成的数据对象。
示例3:基于ModelSerializer(含ForeignKey+ManyToMany):

提示:save方法会返回新生成的数据对象。
7.2 序列化
通过ORM从数据库获取到的 QuerySet 或 对象 均可以被序列化为 json 格式数据。
# models.py
from django.db import models
class Role(models.Model):
""" 角色表 """
title = models.CharField(verbose_name="名称", max_length=32)
class Department(models.Model):
""" 部门表 """
title = models.CharField(verbose_name="名称", max_length=32)
class UserInfo(models.Model):
""" 用户表 """
level_choices = ((1, "普通会员"), (2, "VIP"), (3, "SVIP"),)
level = models.IntegerField(verbose_name="级别", choices=level_choices, default=1)
username = models.CharField(verbose_name="用户名", max_length=32)
password = models.CharField(verbose_name="密码", max_length=64)
age = models.IntegerField(verbose_name="年龄", default=0)
email = models.CharField(verbose_name="邮箱", max_length=64, null=True, blank=True)
token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)
depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE, null=True, blank=True)
roles = models.ManyToManyField(verbose_name="角色", to="Role")
示例1:序列化基本字段

# 切记, 如果从数据库获取的不是QuerySet对象,而是单一对象,例如:
data_object = modes.UserInfo.objects.filter(id=2).first()
ser = UserModelSerializer(instance=data_object,many=False)
print(ser.data)
# views.py
import re
from django.core.validators import EmailValidator
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import exceptions
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = ['username', 'age', 'email']
def validate_email3(self, value):
"""钩子函数,用于验证某个字段"""
if re.match(r"^\w+@\w+\.\w+$", value):
return value
raise exceptions.ValidationError('邮箱格式校验错误')
class UserView(APIView):
"""用户管理"""
def get(self, request):
"""获取用户列表"""
queryset = models.UserInfo.objects.all()
ser = UserModelSerializer(instance=queryset, many=True) # many多条数据
print(ser.data)
return Response({'code': 0, 'data': ser.data})
示例2:自定义字段

# views.py
import re
from django.core.validators import EmailValidator
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import exceptions
from django.forms.models import model_to_dict
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(source='get_level_display')
depart = serializers.CharField(source='depart.title') # 源码里就是 当前类.depart.title
# 额外自定义的字段,通过下面钩子函数展示
roles = serializers.SerializerMethodField()
extra = serializers.SerializerMethodField()
class Meta:
model = models.UserInfo
fields = ['username', 'age', 'email', 'level_text', 'depart', 'roles', 'extra']
def get_roles(self, obj):
data_list = obj.roles.all()
return [model_to_dict(item, ["id", "title"]) for item in data_list]
def get_extra(self, obj):
return 666
class UserView(APIView):
"""用户管理"""
def get(self, request):
"""获取用户列表"""
queryset = models.UserInfo.objects.all()
ser = UserModelSerializer(instance=queryset, many=True) # many多条数据
print(ser.data)
return Response({'code': 0, 'data': ser.data})
示例3:序列化类的嵌套

7.3 数据校验&序列化
上述示例均属于单一功能(要么校验,要么序列化),其实当我们编写一个序列化类既可以做数据校验,也可以做序列化,例如:



# models.py
from django.db import models
class Role(models.Model):
""" 角色表 """
title = models.CharField(verbose_name="名称", max_length=32)
class Department(models.Model):
""" 部门表 """
title = models.CharField(verbose_name="名称", max_length=32)
class UserInfo(models.Model):
""" 用户表 """
level_choices = ((1, "普通会员"), (2, "VIP"), (3, "SVIP"),)
level = models.IntegerField(verbose_name="级别", choices=level_choices, default=1)
username = models.CharField(verbose_name="用户名", max_length=32)
password = models.CharField(verbose_name="密码", max_length=64)
age = models.IntegerField(verbose_name="年龄", default=0)
email = models.CharField(verbose_name="邮箱", max_length=64, null=True, blank=True)
token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)
depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE, null=True, blank=True)
roles = models.ManyToManyField(verbose_name="角色", to="Role")
# urls.py
from django.urls import path, re_path, include
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view()),
]
# views.py
from django.core.validators import EmailValidator
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from app01 import models
class DepartModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Department
fields = ['id', "title"]
extra_kwargs = {
"id": {"read_only": False}, # 数据验证,默认是True
"title": {"read_only": True} # 序列化
}
class RoleModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Role
fields = ['id', "title"]
extra_kwargs = {
"id": {"read_only": False}, # 数据验证
"title": {"read_only": True} # 序列化
}
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(source="get_level_display", read_only=True)
# Serializer嵌套,不是read_only,一定要自定义create和update,自定义新增和更新的逻辑。
depart = DepartModelSerializer(many=False)
roles = RoleModelSerializer(many=True)
extra = serializers.SerializerMethodField(read_only=True)
email2 = serializers.EmailField(write_only=True)
# 数据校验:username、email、email2、部门、角色信息
class Meta:
model = models.UserInfo
fields = [
"username", "age", "email", "level_text", "depart", "roles", "extra", "email2"
]
extra_kwargs = {
"age": {"read_only": True},
"email": {"validators": [EmailValidator, ]},
}
def get_extra(self, obj):
return 666
def validate_username(self, value):
return value
# 新增加数据时
def create(self, validated_data):
""" 如果有嵌套的Serializer,在进行数据校验时,只有两种选择:
1. 将嵌套的序列化设置成 read_only
2. 自定义create和update方法,自定义新建和更新的逻辑,多对多和一对多字段使用
注意:用户端提交数据的格式。
"""
print(validated_data)
depart_id = validated_data.pop('depart')['id']
role_id_list = [ele['id'] for ele in validated_data.pop('roles')]
# 新增用户表
validated_data['depart_id'] = depart_id
user_object = models.UserInfo.objects.create(**validated_data)
# 在用户表和角色表的关联表中添加对应关系
user_object.roles.add(*role_id_list)
return user_object
class UserView(APIView):
""" 用户管理 """
def get(self, request):
""" 展示用户 """
queryset = models.UserInfo.objects.all()
ser = UserModelSerializer(instance=queryset, many=True)
return Response({"code": 0, 'data': ser.data})
def post(self, request):
""" 添加用户 """
ser = UserModelSerializer(data=request.data)
if not ser.is_valid():
return Response({'code': 1006, 'data': ser.errors})
ser.validated_data.pop('email2')
instance = ser.save(age=18, password="123", depart_id=1)
# 新增之后的一个对象(内部调用UserModelSerializer进行序列化)
print(ser.data)
# ser = UserModelSerializer(instance=instance, many=False)
# ser.data
return Response({'code': 0, 'data': ser.data})
底层源码实现:
序列化的底层源码实现有别于上述其他的组件,序列化器相关类的定义和执行都是在视图中被调用的,所以源码的分析过程可以分为:定义类、序列化、数据校验。
源码1:序列化过程


源码2:数据校验过程
/第1部分:drf/assets/image-20210824001814091.png)
/第1部分:drf/assets/image-20210824001844381.png)
8. 视图
8.1 APIView
- View,django
- APIView,drf,在请求到来时,新增了:免除csrf、请求封装、版本、认证、权限、限流的功能。
class GenericAPIView(APIView):
pass # 10功能
class GenericViewSet(xxxx.View-2个功能, GenericAPIView):
pass # 5功能能
class UserView(GenericViewSet):
def get(self,request):
pass
APIView是drf中 “顶层” 的视图类,在他的内部主要实现drf基础的组件的使用,例如:版本、认证、权限、限流等。
# urls.py
from django.urls import path, re_path, include
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view()),
path('api/users/<int:pk>/', views.UserDetailView.as_view()),
]
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
class UserView(APIView):
# 认证、权限、限流等
def get(self, request):
# 业务逻辑:查看列表
return Response({"code": 0, 'data': "..."})
def post(self, request):
# 业务逻辑:新建
return Response({'code': 0, 'data': "..."})
class UserDetailView(APIView):
# 认证、权限、限流等
def get(self, request,pk):
# 业务逻辑:查看某个数据的详细
return Response({"code": 0, 'data': "..."})
def put(self, request,pk):
# 业务逻辑:全部修改
return Response({'code': 0, 'data': "..."})
def patch(self, request,pk):
# 业务逻辑:局部修改
return Response({'code': 0, 'data': "..."})
def delete(self, request,pk):
# 业务逻辑:删除
return Response({'code': 0, 'data': "..."})
8.2 GenericAPIView
GenericAPIView 继承APIView,在APIView的基础上又增加了一些功能。例如:get_queryset、get_object等。
实际在开发中一般不会直接继承它,他更多的是担任 中间人的角色,为子类提供公共功能。
# urls.py
from django.urls import path, re_path, include
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view()),
path('api/users/<int:pk>/', views.UserDetailView.as_view()),
]
# views.py
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
# GenericAPIView内部定义了一些方法,将: - 数据库中获取数据; - 序列化类
# 提取到类变量中,方便定义,内部如果调用 get_queryset、get_serializer就会去读取
class UserView(GenericAPIView):
queryset = models.UserInfo.objects.filter(status=True)
serializer_class = 序列化类
def get(self, request):
# 获取数据库中的数据(读取类变量中的queryset字段)
queryset = self.get_queryset()
# 获取Serializer类并实例化()读取类变量中的serializer_class字段并实例化
ser = self.get_serializer(intance=queryset,many=True)
print(ser.data)
return Response({"code": 0, 'data': "..."})
注意:最大的意义,将数据库查询、序列化类提取到类变量中,后期再提供公共的get/post/put/delete等方法,让开发者只定义类变量,自动实现增删改查。
8.3 GenericViewSet

GenericViewSet类中没有定义任何代码,他就是继承 ViewSetMixin 和 GenericAPIView,也就说他的功能就是将继承的两个类的功能继承到一起。
GenericAPIView,将数据库查询、序列化类的定义提取到类变量中,便于后期处理。ViewSetMixin,将 get/post/put/delete 等方法映射到 list、create、retrieve、update、partial_update、destroy方法中,让视图不再需要两个类。
# urls.py
from django.urls import path, re_path, include
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view({"get":"list","post":"create"})),
path('api/users/<int:pk>/', views.UserView.as_view({
"get":"retrieve",
"put":"update",
"patch":"partial_update",
"delete":"destory"})),
]
# views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.response import Response
class UserView(GenericViewSet):
# 认证、权限、限流等
queryset = models.UserInfo.objects.filter(status=True)
serializer_class = 序列化类
def list(self, request):
# 业务逻辑:查看列表
queryset = self.get_queryset()
ser = self.get_serializer(intance=queryset,many=True)
print(ser.data)
return Response({"code": 0, 'data': "..."})
def create(self, request):
# 业务逻辑:新建
return Response({'code': 0, 'data': "..."})
def retrieve(self, request,pk):
# 业务逻辑:查看某个数据的详细
return Response({"code": 0, 'data': "..."})
def update(self, request,pk):
# 业务逻辑:全部修改
return Response({'code': 0, 'data': "..."})
def partial_update(self, request,pk):
# 业务逻辑:局部修改
return Response({'code': 0, 'data': "..."})
def destory(self, request,pk):
# 业务逻辑:删除
return Response({'code': 0, 'data': "..."})
注意:开发中一般也很少直接去继承他,因为他也属于是 中间人类,在原来 GenericAPIView 基础上又增加了一个映射而已。
8.4 五大类
在drf的为我们提供好了5个用于做 增、删、改(含局部修改)、查列表、查单个数据的5个类(需结合 GenericViewSet 使用)。
# urls.py
from django.urls import path, re_path, include
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view({"get":"list","post":"create"})),
path('api/users/<int:pk>/', views.UserView.as_view({
"get":"retrieve",
"put":"update",
"patch":"partial_update",
"delete":"destroy"})),
]
# views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import (
ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin,
DestroyModelMixin, ListModelMixin
)
class UserView(CreateModelMixin,RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin,ListModelMixin,GenericViewSet):
# 认证、权限、限流等
queryset = models.UserInfo.objects.filter(status=True)
serializer_class = 序列化类
在这个5个类中已帮我们写好了 list、create、retrieve、update、partial_update、destory 方法,我们只需要在根据写 类变量:queryset、serializer_class即可。
list get:获取全部数据,查询页面
create post:创建数据,增加
retrieve get:根据id获取当前用户的对象,查询详细页面
update put:全部更新,serializers中必须提供全部字段,否则报错
partial_update patch:局部更新
destory delete:删除数据
示例1:

# urls.py
from django.urls import path
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view({"get": "list"})),
path('api/users/<int:pk>/', views.UserView.as_view({"get": "retrieve"})),
]
# views.py
from rest_framework import serializers
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(
source="get_level_display",
read_only=True
)
extra = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.UserInfo
fields = ["username", "age", "email", "level_text", "extra"]
def get_extra(self, obj):
return 666
class UserView(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
示例2:

# urls.py
from django.urls import path
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view({"get": "list", "post": "create"})),
path('api/users/<int:pk>/', views.UserView.as_view({"get": "retrieve"})),
]
# views.py
from rest_framework import serializers
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(
source="get_level_display",
read_only=True
)
extra = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.UserInfo
fields = ["username", "age", "email", "level_text", "extra"]
def get_extra(self, obj):
return 666
class UserView(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin, GenericViewSet):
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
def perform_create(self, serializer):
""" 序列化:对请求的数据校验成功后,执行保存。"""
serializer.save(depart_id=1, password="123")
示例3:
# urls.py
from django.urls import path
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view(
{"get": "list", "post": "create"}
)),
path('api/users/<int:pk>/', views.UserView.as_view(
{"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"}
)),
]
# views.py
from rest_framework import serializers
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(
source="get_level_display",
read_only=True
)
extra = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.UserInfo
fields = ["username", "age", "email", "level_text", "extra"]
def get_extra(self, obj):
return 666
class UserView(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericViewSet):
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
def perform_create(self, serializer):
""" 序列化:对请求的数据校验成功后,执行保存。"""
serializer.save(depart_id=1, password="123")
def perform_update(self, serializer):
serializer.save()
def perform_destroy(self, instance):
instance.delete()
示例4:
# urls.py
from django.urls import path
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view(
{"get": "list", "post": "create"}
)),
path('api/users/<int:pk>/', views.UserView.as_view(
{"get": "retrieve",
"put": "update",
"patch": "partial_update",
"delete": "destroy"}
)),
]
# views.py
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(
source="get_level_display",
read_only=True
)
extra = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.UserInfo
fields = ["username", "age", "email", "level_text", "extra"]
def get_extra(self, obj):
return 666
class UserView(ModelViewSet):
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
def perform_create(self, serializer):
""" 序列化:对请求的数据校验成功后,执行保存。"""
serializer.save(depart_id=1, password="123")
补充:
# views.py
from rest_framework import serializers
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = ["username", "age"]
class UserModelSerializer2(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = ["username", "age", "email", "level_text"]
class UserView(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin, GenericViewSet):
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
def perform_create(self, serializer):
""" 序列化:对请求的数据校验成功后,执行保存。"""
serializer.save(depart_id=1, password="123")
def get_serializer(self):
"""不同的请求使用不同的序列化类,重写get_serializer方法"""
if self.request.method == 'POST':
return UserModelSerializer2
else:
return UserModelSerializer2
在开发过程中使用 五大类 或 ModelViewSet 是比较常见的,并且如果他们内部的某些功能不够用,还可以进行重新某些方法进行扩展。
问题:drf中提供了这么多视图,以后那个用的比较多?
-
接口与数据库操作无关,直接继承APIView,比如redis,mqrabbit
-
接口背后需要对数据库进行操作,一般:
ModelViewSet或CreateModelMixin、ListModelMixin...- 利用钩子自定义功能。 - 重写某个写方法,实现更加完善的功能。 -
根据自己公司的习惯,自定义 :
ModelViewSet或CreateModelMixin、ListModelMixin...
补充:权限
在之前定义权限类时,类中可以定义两个方法:has_permission 和 has_object_permission
has_permission,在请求进入视图之前就会执行。has_object_permission,当视图中调用self.get_object时就会被调用(删除、更新、查看某个对象时都会调用),一般用于检查对某个对象是否具有权限进行操作。
class PermissionA(BasePermission):
message = {"code": 1003, 'data': "无权访问"}
def has_permission(self, request, view):
exists = request.user.roles.filter(title="员工").exists()
if exists:
return True
return False
def has_object_permission(self, request, view, obj):
# 当前用户是否有权限访问此对象
return True
所以,让我们在编写视图类时,如果是直接获取间接继承了 GenericAPIView,同时内部调用 get_object方法,这样在权限中通过 has_object_permission 就可以进行权限的处理。
9. 条件搜索
如果某个API需要传递一些条件进行搜索,其实就在是URL后面通过GET传参即可,例如:
/api/users?age=19&category=12
在drf中也有相应组件可以支持条件搜索。
9.1 自定义Filter

# urls.py
from django.urls import path
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view(
{"get": "list", "post": "create"}
)),
path('api/users/<int:pk>/', views.UserView.as_view(
{"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"}
)),
]
# views.py
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from rest_framework.filters import BaseFilterBackend
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(
source="get_level_display",
read_only=True
)
extra = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.UserInfo
fields = ["username", "age", "email", "level_text", "extra"]
def get_extra(self, obj):
return 666
class Filter1(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
age = request.query_params.get('age')
if not age:
return queryset
return queryset.filter(age=age)
class Filter2(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
user_id = request.query_params.get('id')
if not user_id:
return queryset
return queryset.filter(id__gt=user_id)
class UserView(ModelViewSet):
filter_backends = [Filter1, Filter2]
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
def perform_create(self, serializer):
""" 序列化:对请求的数据校验成功后,执行保存。"""
serializer.save(depart_id=1, password="123")
9.2 第三方Filter
在drf开发中有一个常用的第三方过滤器:DjangoFilterBackend。
pip install django-filter
注册app:
INSTALLED_APPS = [
...
'django_filters',
...
]
视图配置和应用(示例1):
# views.py
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(
source="get_level_display",
read_only=True
)
extra = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.UserInfo
fields = ["username", "age", "email", "level_text", "extra"]
def get_extra(self, obj):
return 666
class UserView(ModelViewSet):
filter_backends = [DjangoFilterBackend, ]
filterset_fields = ["id", "age", "email"]
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
def perform_create(self, serializer):
""" 序列化:对请求的数据校验成功后,执行保存。"""
serializer.save(depart_id=1, password="123")
视图配置和应用(示例2):
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from django_filters import FilterSet, filters
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(
source="get_level_display",
read_only=True
)
depart_title = serializers.CharField(
source="depart.title",
read_only=True
)
extra = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.UserInfo
fields = ["id", "username", "age", "email", "level_text", "extra", "depart_title"]
def get_extra(self, obj):
return 666
class MyFilterSet(FilterSet):
depart = filters.CharFilter(field_name="depart__title", lookup_expr="exact")
min_id = filters.NumberFilter(field_name='id', lookup_expr='gte') # lookup_expr 内部搜索条件
class Meta:
model = models.UserInfo
fields = ["min_id", "depart"]
class UserView(ModelViewSet):
filter_backends = [DjangoFilterBackend, ]
filterset_class = MyFilterSet
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
def perform_create(self, serializer):
""" 序列化:对请求的数据校验成功后,执行保存。"""
serializer.save(depart_id=1, password="123")
视图配置和应用(示例3):
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend, OrderingFilter
from django_filters import FilterSet, filters
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(
source="get_level_display",
read_only=True
)
depart_title = serializers.CharField(
source="depart.title",
read_only=True
)
extra = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.UserInfo
fields = ["id", "username", "age", "email", "level_text", "extra", "depart_title"]
def get_extra(self, obj):
return 666
class MyFilterSet(FilterSet):
# /api/users/?min_id=2 -> id>=2
min_id = filters.NumberFilter(field_name='id', lookup_expr='gte')
# /api/users/?name=wupeiqi -> not ( username=wupeiqi )
name = filters.CharFilter(field_name="username", lookup_expr="exact", exclude=True)
# /api/users/?depart=xx -> depart__title like %xx%
depart = filters.CharFilter(field_name="depart__title", lookup_expr="contains")
# /api/users/?token=true -> "token" IS NULL
# /api/users/?token=false -> "token" IS NOT NULL
token = filters.BooleanFilter(field_name="token", lookup_expr="isnull")
# /api/users/?email=xx -> email like xx%
email = filters.CharFilter(field_name="email", lookup_expr="startswith")
# /api/users/?level=2&level=1 -> "level" = 1 OR "level" = 2(必须的是存在的数据,否则报错-->内部有校验机制)
# level = filters.AllValuesMultipleFilter(field_name="level", lookup_expr="exact")
level = filters.MultipleChoiceFilter(field_name="level", lookup_expr="exact", choices=models.UserInfo.level_choices)
# /api/users/?age=18,20 -> age in [18,20]
age = filters.BaseInFilter(field_name='age', lookup_expr="in")
# /api/users/?range_id_max=10&range_id_min=1 -> id BETWEEN 1 AND 10
range_id = filters.NumericRangeFilter(field_name='id', lookup_expr='range')
# /api/users/?ordering=id -> order by id asc
# /api/users/?ordering=-id -> order by id desc
# /api/users/?ordering=age -> order by age asc
# /api/users/?ordering=-age -> order by age desc
ordering = filters.OrderingFilter(fields=["id", "age"])
# /api/users/?size=1 -> limit 1(自定义搜索)
size = filters.CharFilter(method='filter_size', distinct=False, required=False)
class Meta:
model = models.UserInfo
fields = ["id", "min_id", "name", "depart", "email", "level", "age", 'range_id', "size", "ordering"]
def filter_size(self, queryset, name, value):
int_value = int(value)
return queryset[0:int_value]
class UserView(ModelViewSet):
filter_backends = [DjangoFilterBackend, ]
filterset_class = MyFilterSet
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
def perform_create(self, serializer):
""" 序列化:对请求的数据校验成功后,执行保存。"""
serializer.save(depart_id=1, password="123")
lookup_expr有很多常见选择:
'exact': _(''), 精确匹配,不忽略大小写
'iexact': _(''), 精确匹配,忽略大小写
'contains': _('contains'),
'icontains': _('contains'),
'startswith': _('starts with'),
'istartswith': _('starts with'),
'endswith': _('ends with'),
'iendswith': _('ends with'),
'gt': _('is greater than'),
'gte': _('is greater than or equal to'),
'lt': _('is less than'),
'lte': _('is less than or equal to'),
'in': _('is in'),
'range': _('is in range'), between...and
'isnull': _(''),
'regex': _('matches regex'),
'iregex': _('matches regex'),
全局配置和应用:
# settings.py 全局配置
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend',]
}
9.3 内置Filter
drf源码中内置了2个filter,分别是:
-
OrderingFilter,支持排序。
from rest_framework import serializers from rest_framework.viewsets import ModelViewSet from app01 import models from rest_framework.filters import OrderingFilter class UserModelSerializer(serializers.ModelSerializer): level_text = serializers.CharField( source="get_level_display", read_only=True ) depart_title = serializers.CharField( source="depart.title", read_only=True ) extra = serializers.SerializerMethodField(read_only=True) class Meta: model = models.UserInfo fields = ["id", "username", "age", "email", "level_text", "extra", "depart_title"] def get_extra(self, obj): return 666 class UserView(ModelViewSet): filter_backends = [OrderingFilter, ] # ?order=id # ?order=-id # ?order=age ordering_fields = ["id", "age"] queryset = models.UserInfo.objects.all() serializer_class = UserModelSerializer def perform_create(self, serializer): """ 序列化:对请求的数据校验成功后,执行保存。""" serializer.save(depart_id=1, password="123") -
SearchFilter,支持模糊搜索。
from rest_framework import serializers from rest_framework.viewsets import ModelViewSet from app01 import models from rest_framework.filters import SearchFilter class UserModelSerializer(serializers.ModelSerializer): level_text = serializers.CharField( source="get_level_display", read_only=True ) depart_title = serializers.CharField( source="depart.title", read_only=True ) extra = serializers.SerializerMethodField(read_only=True) class Meta: model = models.UserInfo fields = ["id", "username", "age", "email", "level_text", "extra", "depart_title"] def get_extra(self, obj): return 666 class UserView(ModelViewSet): # ?search=武沛%齐 filter_backends = [SearchFilter, ] search_fields = ["id", "username", "age"] queryset = models.UserInfo.objects.all() serializer_class = UserModelSerializer def perform_create(self, serializer): """ 序列化:对请求的数据校验成功后,执行保存。""" serializer.save(depart_id=1, password="123")"app01_userinfo"."id" LIKE %武沛齐% ESCAPE '\' OR "app01_userinfo"."username" LIKE %武沛齐% ESCAPE '\' OR "app01_userinfo"."age" LIKE %武沛齐% ESCAPE '\'
10. 分页
在查看数据列表的API中,如果 数据量 比较大,肯定不能把所有的数据都展示给用户,而需要通过分页展示。
在drf中为我们提供了一些分页先关类:
BasePagination,分页基类
PageNumberPagination(BasePagination) 支持 /accounts/?page=4&page_size=100 格式的分页
LimitOffsetPagination(BasePagination) 支持 ?offset=100&limit=10 格式的分页
CursorPagination(BasePagination) 支持 上一下 & 下一页 格式的分页(不常用)
10.1 APIView视图
如果编写视图是直接继承APIView,那么在使用分页时,就必须自己手动 实例化 和 调用相关方法。
1.PageNumberPagination


from rest_framework import serializers
from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import models
from rest_framework.pagination import PageNumberPagination
class UserModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = ['id','username','age','email']
class MyPageNumberPagination(PageNumberPagination):
page_size_query_param = 'size' # 在url传的size 是用户自定义一页显示多少条数据
page_size = 2
max_page_size = 100
class UserView(APIView):
def get(self,reuquest,*args,**kwargs):
queryset = models.UserInfo.objects.all().order_by('-id')
pager = PageNumberPagination()
paginate_queryset = pager.paginate_queryset(queryset,reuquest,self)
ser = UserModelSerializer(instance=paginate_queryset,many=True)
return Response(ser.data)
2.LimitOffsetPagination

3.CursorPagination

10.2 GenericAPIView派生类
如果是使用 ListModelMixin 或 ModelViewSet ,则只需要配置相关类即可,内部会自动执行相关分页的方法。
1.PageNumberPagination

2.LimitOffsetPagination

3.CursorPagination

11. 路由
在之前进行drf开发时,对于路由我们一般进行两种配置:
-
视图继承APIView
from django.urls import path from app01 import views urlpatterns = [ path('api/users/', views.UserView.as_view()), ] -
视图继承
ViewSetMixin(GenericViewSet、ModelViewSet)from django.urls import path, re_path, include from app01 import views urlpatterns = [ path('api/users/', views.UserView.as_view({"get":"list","post":"create"})), path('api/users/<int:pk>/', views.UserView.as_view({"get":"retrieve","put":"update","patch":"partial_update","delete":"destory"})), ]对于这种形式的路由,drf中提供了更简便的方式:
from rest_framework import routers from app01 import views router = routers.SimpleRouter() router.register(r'api/users', views.UserView) urlpatterns = [ # 其他URL # path('xxxx/', xxxx.as_view()), ] urlpatterns += router.urls也可以利用include,给URL加前缀:
from django.urls import path, include from rest_framework import routers from app01 import views router = routers.SimpleRouter() router.register(r'users', views.UserView) urlpatterns = [ path('api/', include((router.urls, 'app_name'), namespace='instance_name')), # 其他URL # path('forgot-password/', ForgotPasswordFormView.as_view()), ]
12. 解析器
之前使用 request.data 获取请求体中的数据。
这个 reqeust.data 的数据怎么来的呢?其实在drf内部是由解析器,根据请求者传入的数据格式 + 请求头来进行处理。
1.JSONParser

2.FormParser

3.MultiPartParser

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/test/" method="post" enctype="multipart/form-data">
<input type="text" name="user" />
<input type="file" name="img">
<input type="submit" value="提交">
</form>
</body>
</html>
4.FileUploadParser

解析器可以设置多个,默认解析器:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser, JSONParser, FormParser
# drf 默认的解析器
class UserView(APIView):
parser_classes = [JSONParser,FormParser,MultiPartPaerser]
def post(self, request):
print(request.content_type)
print(request.data)
return Response("...")
drf实战案例
1. 需求
请结合上述学习的drf知识开发 简易版《抽屉新热榜》。其中包含的功能如下:
-
注册
输入:手机号、用户名、密码、确认密码。 -
登录
输入:手机号 或 用户名 + 密码 注意:登录成功后给用户返回token,后续请求需要在url中携带token(有效期2周) -
我的话题
- 我的话题列表 - 创建话题 - 修改话题 - 删除话题(逻辑删除) -
我的资讯
- 创建资讯(5分钟创建一个,需要根据用户限流) 问题1:5/h 2/m; 问题2:成功后,下次再创建; - 文本(你问我答、42区、挨踢1024、段子) - 图片(图片、你问我答、42区、挨踢1024、段子) - 连接(图片、你问我答、42区、挨踢1024、段子) 注意:创建时默认自己做1个推荐。 - 我的资讯列表 -
首页
- 资讯首页 - 时间倒序,读取已审核通过的资讯 - 加载更多,分页处理 - 支持传入参数,查询各分区资讯:图片、你问我答、42区、挨踢1024、段子 ?zone=2 -
推荐
- 推荐 - 取消推荐 - 我的推荐列表 -
收藏
- 收藏 or 取消收藏 - 我的收藏列表 -
评论
- 查看评论列表 - 根据【后代的更新时间】从大到小排序,读取根评论,每次读20条。 - 读取根评论先关的子评论。 - 将子评论挂靠到跟评论上,最终形成父子关系通过JSON返回给前端。 注意:自己也可以通过depth实现逐步读取子评论(此处不这样操作) - 创建评论 - 判断是根评论 or 回复 - 回复时,深度+1 - 评论后,找到根评论去更新【后代的更新时间】
2. 参考表结构
表结构参考:
from django.db import models
class DeletedModel(models.Model):
deleted = models.BooleanField(verbose_name="已删除", default=False)
class Meta:
abstract = True
class UserInfo(DeletedModel):
""" 用户表 """
username = models.CharField(verbose_name="用户名", max_length=32)
phone = models.CharField(verbose_name="手机号", max_length=32, db_index=True)
password = models.CharField(verbose_name="密码", max_length=64)
token = models.CharField(verbose_name="token", max_length=64, null=True, blank=True, db_index=True)
token_expiry_date = models.DateTimeField(verbose_name="token有效期", null=True, blank=True)
status_choice = (
(1, "激活"),
(2, "禁用"),
)
status = models.IntegerField(verbose_name="状态", choices=status_choice, default=1)
create_datetime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)
class Meta:
# The newer indexes option provides more functionality than index_together
# index_together may be deprecated in the future.
# https://docs.djangoproject.com/en/3.2/ref/models/options/#index-together
# 联合索引,django3之后的推荐用法
indexes = [
models.Index(fields=['username', "password"], name='idx_name_pwd')
]
class Topic(DeletedModel):
""" 话题 """
title = models.CharField(verbose_name="话题", max_length=16, db_index=True)
is_hot = models.BooleanField(verbose_name="热门话题", default=False)
user = models.ForeignKey(verbose_name="用户", to="UserInfo", on_delete=models.CASCADE)
create_datetime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)
class News(DeletedModel):
""" 新闻资讯 """
zone_choices = ((1, "42区"), (2, "段子"), (3, "图片"), (4, "挨踢1024"), (5, "你问我答"))
zone = models.IntegerField(verbose_name="专区", choices=zone_choices)
title = models.CharField(verbose_name="文字", max_length=150)
url = models.CharField(verbose_name="链接", max_length=200, null=True, blank=True)
# xxxxx?xxxxxx.png,xxxxxxxx.jeg
image = models.TextField(verbose_name="图片地址", help_text="逗号分割", null=True, blank=True)
topic = models.ForeignKey(verbose_name="话题", to="Topic", on_delete=models.CASCADE, null=True, blank=True)
user = models.ForeignKey(verbose_name="用户", to="UserInfo", on_delete=models.CASCADE)
create_datetime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)
status_choice = (
(1, "待审核"),
(2, "已通过"),
(3, "未通过"),
)
status = models.IntegerField(verbose_name="状态", choices=status_choice, default=1)
collect_count = models.IntegerField(verbose_name="收藏数", default=0)
recommend_count = models.IntegerField(verbose_name="推荐数", default=0)
comment_count = models.IntegerField(verbose_name="评论数", default=0)
class Collect(models.Model):
""" 收藏 """
news = models.ForeignKey(verbose_name="资讯", to="News", on_delete=models.CASCADE)
user = models.ForeignKey(verbose_name="用户", to="UserInfo", on_delete=models.CASCADE)
create_datetime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)
class Meta:
# unique_together = [['news', 'user']]
# 联合唯一索引
constraints = [
models.UniqueConstraint(fields=['news', 'user'], name='uni_collect_news_user')
]
class Recommend(models.Model):
""" 推荐 """
news = models.ForeignKey(verbose_name="资讯", to="News", on_delete=models.CASCADE)
user = models.ForeignKey(verbose_name="用户", to="UserInfo", on_delete=models.CASCADE)
create_datetime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['news', 'user'], name='uni_recommend_news_user')
]
class Comment(models.Model):
""" 评论表 """
news = models.ForeignKey(verbose_name="资讯", to="News", on_delete=models.CASCADE)
user = models.ForeignKey(verbose_name="用户", to="UserInfo", on_delete=models.CASCADE)
content = models.CharField(verbose_name="内容", max_length=150)
depth = models.IntegerField(verbose_name="深度", default=0)
root = models.ForeignKey(verbose_name="根评论", to="Comment", related_name="descendant", on_delete=models.CASCADE,
null=True, blank=True)
reply = models.ForeignKey(verbose_name="回复", to="Comment", related_name="reply_list", on_delete=models.CASCADE,
null=True, blank=True)
create_datetime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)
# 针对根评论
descendant_update_datetime = models.DateTimeField(verbose_name="后代更新时间", auto_now_add=True)
3. 案例讲解
3.1 环境准备
-
基于django创建项目,例如:dig
-
安装必备模块
django-filter==2.4.0 django-redis==5.0.0 djangorestframework==3.12.4 -
创建app,例如:api
-
注册app,设置版本
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'django_filters', 'api.apps.ApiConfig' ] REST_FRAMEWORK = { # 版本配置 "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning", "DEFAULT_VERSION": "v1", "ALLOWED_VERSIONS": ["v1"], "VERSION_PARAM": "version", # # 认证配置 "DEFAULT_AUTHENTICATION_CLASSES": ["api.extension.auth.TokenAuthentication", ], "UNAUTHENTICATED_USER": lambda: None, "UNAUTHENTICATED_TOKEN": lambda: None, # # 分页配置 "DEFAULT_PAGINATION_CLASS": "api.extension.page.DigLimitOffsetPagination" } -
拷贝表结构到 models.py 并 生成数据库
目录结构

路由配置
# dig.urls.py
from django.urls import path, include
urlpatterns = [
path('api/', include('api.urls')),
]
# api.urls.py
from django.urls import path
from rest_framework import routers
from api.views import account, topic, news, collect, recommend, comment
router = routers.SimpleRouter()
# 这里要编写别名,不然视图类会因为找不到类名无法生成url名报错
router.register(r'register', account.RegisterView, 'register')
# 创建话题(认证)
router.register(r'topic', topic.TopicView)
# 我的资讯
router.register(r'news', news.NewsView)
# 资讯首页
router.register(r'index', news.IndexView)
# 收藏
router.register(r'collect', collect.CollectView)
# 推荐
router.register(r'recommend', recommend.RecommendView)
# 评论
router.register(r'comment', comment.CommentView)
urlpatterns = [
# path('register/', account.RegisterView.as_view({"post": "create"})),
path('auth/', account.AuthView.as_view()),
]
urlpatterns += router.urls

浙公网安备 33010602011771号