文章目录
我的网站: https://pythoneers.cn
1. API接口开发
1.1 获取数据的接口
获取所有的字段:
1from rest_framework import views, serializers, response 2from .. import models 3 4class UserSerializer(serializers.ModelSerializer): 5 class Meta: 6 model = models.User 7 fields = "__all__" 8 9class User(views.APIView): 10 def get(self, request): 11 qs = models.User.objects.all() 12 serializer = UserSerializer(qs, many=True) 13 return response.Response({ 14 15 16 17 'status': 0, 18 'data': serializer.data 19 })

获取部分字段:
1from rest_framework import views, serializers, response 2from .. import models 3 4class UserSerializer(serializers.ModelSerializer): 5 class Meta: 6 model = models.User 7 fields = ('id', 'name') # 只需要修改这里 8 9class User(views.APIView): 10 def get(self, request): 11 qs = models.User.objects.all() 12 serializer = UserSerializer(qs, many=True) 13 return response.Response({ 14 15 16 17 'status': 0, 18 'data': serializer.data 19 })

获取单条数据:
1from rest_framework import views, serializers, response 2from .. import models 3 4class UserSerializer(serializers.ModelSerializer): 5 class Meta: 6 model = models.User 7 fields = ('id', 'name') 8 9class User(views.APIView): 10 def get(self, request): 11 qs = models.User.objects.all() 12 serializer = UserSerializer(qs.first(), many=False) # 默认是False,修改这里 13 return response.Response({ 14 15 16 17 'status': 0, 18 'data': serializer.data 19 })

1.2 添加数据的接口
添加数据:
1from rest_framework import views, serializers, response 2from .. import models 3 4class UserSerializer(serializers.ModelSerializer): 5 class Meta: 6 model = models.User 7 fields = "__all__" 8 9class User(views.APIView): 10 def post(self, request): 11 serializer = UserSerializer(data=request.data) 12 result = { 13 14 15 'status': 0, 'data': None} 16 if serializer.is_valid(): 17 instance = serializer.save() 18 result['data'] = instance.pk 19 return response.Response(result) 20 else: 21 result['status'] = 1 22 result['data'] = serializer.errors 23 return result

1.3 更新数据的接口
更新数据的接口:
1from rest_framework import views, serializers, response 2from .. import models 3 4class UserSerializer(serializers.ModelSerializer): 5 class Meta: 6 model = models.User 7 fields = "__all__" 8 9class UserDetail(views.APIView): 10 def put(self, request, pk): 11 instance = models.User.objects.filter(pk=pk).first() 12 result = { 13 14 15 'status': 0, 'data': None} 16 if not instance: 17 result['status'] = 1 18 result['data'] = '数据不存在!' 19 else: 20 # 如果没有传入instance,默认是None,表示添加数据而不是更新数据 21 serializer = UserSerializer(instance=instance, data=request.data) 22 if serializer.is_valid(): 23 instance = serializer.save() 24 result['data'] = instance.pk 25 else: 26 result['status'] = 1 27 result['data'] = serializer.errors 28 return response.Response(result)
没有查询到要更新的数据:

更新数据成功:

1.4 删除数据的接口
删除数据的接口:
1from rest_framework import views, serializers, response 2from .. import models 3 4class UserSerializer(serializers.ModelSerializer): 5 class Meta: 6 model = models.User 7 fields = "__all__" 8 9class UserDetail(views.APIView): 10 def delete(self, request, pk): 11 instance = models.User.objects.filter(pk=pk) 12 result = { 13 14 15 'status': 0, 'data': None} 16 if not instance: 17 result['status'] = 1 18 result['data'] = '数据不存在,无法删除!' 19 else: 20 instance.delete() 21 result['data'] = [] 22 return response.Response(result)
删除的数据不存在:

删除数据成功:

2. API字段的定制
表中的字段有时候是不能满足我们实际需要的,所以大多情况下我们需要 加工从数据库中获取到的字段,以及扩展更多的字段。也可能获取别的表的字段进行数据加工生成新字段,响应给前端让它做数据渲染。下面是对字段处理的几种方式:
2.1 别名字段
这种是最简单的,返回给客户端的字段和原字段的字段名不同,但是 值相同:
1class UserSerializer(serializers.ModelSerializer): 2 name = serializers.CharField(max_length=5, error_messages={ 3 4 5 'max_length': '字段太长!'}) 6 alias = serializers.CharField(source='name') # 注意这里source='name' 7 8 class Meta: 9 model = models.User 10 fields = ('name', 'alias') 11 12class User(views.APIView): 13 def get(self, request): 14 qs = models.User.objects.all() 15 serializer = UserSerializer(qs, many=True) 16 return response.Response({ 17 18 19 20 'status': 0, 21 'data': serializer.data 22 })

2.2 字段格式化
对从数据库中获取的日起进行格式化,数据库中格式是:2020-06-20 03:32:28.376772,格式化之后是:2020-06-20 03:32:28,
1class UserSerializer(serializers.ModelSerializer): 2 name = serializers.CharField(max_length=5, error_messages={ 3 4 5 'max_length': '字段太长!'}) 6 alias = serializers.CharField(source='name') 7 register_date = serializers.DateTimeField(format='%Y-%m-%d %X') # 在这里进行的格式化 8 9 class Meta: 10 model = models.User 11 fields = ('name', 'alias', 'register_date') 12 13class User(views.APIView): 14 def get(self, request): 15 qs = models.User.objects.all() 16 serializer = UserSerializer(qs, many=True) 17 return response.Response({ 18 19 20 21 'status': 0, 22 'data': serializer.data 23 })

2.3 字段创建
字段别名的方式可以认为是创建新的字段,但是它还是依赖于原字段,不是真正意义上的创建新字段:
1class UserSerializer(serializers.ModelSerializer): 2 name = serializers.CharField(max_length=5, error_messages={ 3 4 5 'max_length': '字段太长!'}) 6 alias = serializers.CharField(source='name') 7 register_date = serializers.DateTimeField(format='%Y-%m-%d %X') 8 """ 9 创建新字段,自定义名字是id 10 """ 11 # id = serializers.SerializerMethodField(method_name='get_id') # 可以指定使用的方法名称 12 id = serializers.SerializerMethodField() # 可以不指定默认是:get_id,需要写get_id方法 13 14 def get_id(self, instance): 15 """ 16 :param instance: 查询的结果(直接写上instance就可以获取instance) 17 :return: 18 """ 19 return instance.pk 20 21 class Meta: 22 model = models.User 23 fields = ('name', 'alias', 'register_date', 'id') 24 25class User(views.APIView): 26 def get(self, request): 27 qs = models.User.objects.all() 28 serializer = UserSerializer(qs, many=True) 29 return response.Response({ 30 31 32 33 'status': 0, 34 'data': serializer.data 35 })

2.4 字段加工
一般情况下,网站的图片使用CDN加速,CDN可能来自不同的CDN服务提供商。如果要换CDN服务提供商,那么接口返回的所有图片链接都需要更新。如果我们对链接部分进行处理,让链接的域名部分可以独立出来,这样如果换提供商,直接修改域名就可以了,下面是个简单的示例:
1class ImagePathField(serializers.Field): 2 def __init__(self, domain=None, *args, **kwargs): 3 self.domain = domain 4 super(ImagePathField, self).__init__(*args, **kwargs) 5 6 def to_representation(self, value): 7 if not self.domain: 8 self.domain = 'www.aistudies.com.cn' 9 # return '{}' + '{}.png'.format(self.domain, value) 10 return f'{self.domain}/{value}.jpg' 11 12 def to_internal_value(self, data): 13 return data 14 15class UserSerializer(serializers.ModelSerializer): 16 name = serializers.CharField(max_length=5, error_messages={ 17 18 19 'max_length': '字段太长!'}) 20 alias = serializers.CharField(source='name') 21 register_date = serializers.DateTimeField(format='%Y-%m-%d %X') 22 images = serializers.CharField() 23 # 如果source=None,则返回给客户端的img是null 24 img = ImagePathField(domain='www.baidu.com', source='images') 25 26 def get_id(self, instance): 27 """ 28 :param instance: 查询的结果(直接写上instance就可以获取instance) 29 :return: 30 """ 31 return instance.pk 32 33 class Meta: 34 model = models.User 35 fields = ('name', 'alias', 'register_date', 'id', 'images', 'img') 36 37class User(views.APIView): 38 def get(self, request): 39 qs = models.User.objects.all() 40 serializer = UserSerializer(qs, many=True) 41 return response.Response({ 42 43 44 45 'status': 0, 46 'data': serializer.data 47 })

只是作为例子使用了下,当然这里还是有很多问题的!
3. DRF认证
3.1 自定义认证
有些API不需要用户登录就可以访问,但是有些需要用户登录才可以访问。Django REST framework中内置认证组件,可以实现需要用户登录才可以访问API的功能。借助内置认证组件,可以方便地自定义认证规则:
models.py
1from django.db import models 2 3# Create your models here. 4class User(models.Model): 5 name = models.CharField(max_length=32, unique=True) 6 pwd = models.CharField(max_length=64) 7 user_type_choices = ( 8 (1, '普通用户'), 9 (2, 'VIP'), 10 (3, 'SVIP') 11 ) 12 user_type = models.IntegerField(choices=user_type_choices) 13 14class UserToken(models.Model): 15 token = models.CharField(max_length=64) 16 user = models.OneToOneField(to='User',on_delete=models.CASCADE)

写一些数据,用于登录认证测试:

setting.py
1DATABASES = { 2 3 4 5 'default': { 6 7 8 9 'ENGINE': 'django.db.backends.mysql', 10 'NAME': 'drftest', 11 'USER': 'root', 12 'PASSWORD': '123456', 13 'HOST': 'localhost', 14 'PORT': '3306' 15 }, 16 'mysql': { 17 18 19 20 'ENGINE': 'django.db.backends.sqlite3', 21 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 22 } 23}
views.py:
1import hashlib, time 2from rest_framework.views import APIView, exceptions 3from .models import User, UserToken 4from django.http import JsonResponse 5 6# Create your views here. 7def md5(name): 8 obj = hashlib.md5(bytes(name, encoding='utf-8')) 9 ctime = str(time.time()) 10 obj.update(bytes(ctime, encoding='utf-8')) 11 return obj.hexdigest() 12 13class Authenticate: 14 def authenticate(self, request): 15 token = request._request.GET.get('token') 16 print(token) 17 token_obj = UserToken.objects.filter(token=token).first() 18 if not token_obj: 19 raise exceptions.AuthenticationFailed('用户认证失败!') 20 # 在rest framework内部会将整个两个字段赋值给request,共后续操作使用 21 # 有三种返回值,None:表示不管;异常:没有通过认证;元组:返回下面两个元素,一个给request.use,一个给request.auth 22 return (token_obj.user, token_obj) # (request.user,request.auth) 23 24 def authenticate_header(self, request): 25 pass 26 27class AuthView(APIView): 28 def post(self, request, *args, **kwargs): 29 """ 30 用户登录成功后,返回根据时间辍生成的token,每次登录的时间不同,每次生成的token也不同,都被记录到token表中用于与每次请求带着的token进行对比。如果对比成功,则认证成功,是允许访问的。 31 :param request: 32 :param args: 33 :param kwargs: 34 :return: 35 """ 36 ret = { 37 38 39 'code': 1000, 'msg': None} 40 try: 41 # 需要以form-data的方式提交 42 name = request._request.POST.get('name') 43 pwd = request._request.POST.get('pwd') 44 instance = User.objects.filter(name=name, pwd=pwd).first() # User object (1), 45 print(type(instance)) # <class 'app.models.User'>,加不加all()结果一样 46 print(instance) # User object (1),加不加all()结果一样 47 if not instance: 48 ret['code'] = 1001 49 ret['msg'] = '用户名或密码错误' 50 else: 51 token = md5(name=name) 52 UserToken.objects.update_or_create(user=instance, defaults={ 53 54 55 'token': token}) 56 ret['token'] = token 57 except Exception as e: 58 ret['code'] = 1001 59 ret['msg'] = '请求异常' 60 return JsonResponse(ret) 61 62class OrderView(APIView): 63 # 列表中有写认证类则需要认证,使用自定义的Authenticate类来认证 64 authentication_classes = [Authenticate, ] 65 66 def get(self, request, *args, **kwargs): 67 # request.user 68 # request.auth 69 self.dispatch 70 order_dict = { 71 72 73 74 1: { 75 76 77 78 'name': "thanlon", 79 'age': 24, 80 'gender': '男', 81 }, 82 2: { 83 84 85 86 'name': "kiku", 87 'age': 26, 88 'gender': '女', 89 }, 90 } 91 # token = request._request.GET.get('token') 92 ret = { 93 94 95 'code': 1000, "msg": None, 'data': None} 96 try: 97 ret['data'] = order_dict 98 except Exception as e: 99 pass 100 return JsonResponse(ret)
登录时生成的token:

用于用户登录成功生成token的类AuthView不需要认证,OrderView类需要认证,如果不带token访问这个接口会返回失败的认证:

带着这token来访问这个接口,注意这里从url中获取的,要把token放在url上,不要放到请求头发送过去。结果发现可以访问到请求的数据:

也可以放到请求头中发过去,认证类获取token的时候要到请求头中获取。
3.2 认证流程
想要更好地使用认证组件,不得不研究和学习下认证组件的实现原理:















认证可以加多个,一般不会使用到多个认证。列表中的认证类中从第一个开始,如果第一个认证没有做处理,返回None,则交给下一个认证处理:
1import hashlib, time 2from rest_framework.views import APIView, exceptions 3from .models import User, UserToken 4from django.http import JsonResponse 5 6# Create your views here. 7def md5(name): 8 obj = hashlib.md5(bytes(name, encoding='utf-8')) 9 ctime = str(time.time()) 10 obj.update(bytes(ctime, encoding='utf-8')) 11 return obj.hexdigest() 12 13class Authenticate: 14 def authenticate(self, request): 15 token = request._request.GET.get('token') 16 print(token) 17 token_obj = UserToken.objects.filter(token=token).first() 18 if not token_obj: 19 raise exceptions.AuthenticationFailed('用户认证失败!') 20 # 在rest framework内部会将整个两个字段赋值给request,共后续操作使用 21 return (token_obj.user, token_obj) # (request.name,request.auth) 22 23 def authenticate_header(self, request): 24 pass 25 26class FirstAuthenticate: 27 def authenticate(self, request): 28 pass 29 30 def authenticate_header(self, request): 31 pass 32 33class AuthView(APIView): 34 def post(self, request, *args, **kwargs): 35 print(md5('thanlon')) 36 ret = { 37 38 39 'code': 1000, 'msg': None} 40 try: 41 # 需要以form-data的方式提交 42 name = request._request.POST.get('name') 43 pwd = request._request.POST.get('pwd') 44 instance = User.objects.filter(name=name, pwd=pwd).first() # User object (1), 45 print(type(instance)) # <class 'app.models.User'>,加不加all()结果一样 46 print(instance) # User object (1),加不加all()结果一样 47 if not instance: 48 ret['code'] = 1001 49 ret['msg'] = '用户名或密码错误' 50 else: 51 token = md5(name=name) 52 UserToken.objects.update_or_create(user=instance, defaults={ 53 54 55 'token': token}) 56 ret['token'] = token 57 except Exception as e: 58 ret['code'] = 1001 59 ret['msg'] = '请求异常' 60 return JsonResponse(ret) 61 62class OrderView(APIView): 63 # 需要认证,使用自定义的Authenticate类来认证 64 authentication_classes = [FirstAuthenticate, Authenticate, ] 65 66 def get(self, request, *args, **kwargs): 67 # request.name 68 # request.auth 69 self.dispatch 70 order_dict = { 71 72 73 74 1: { 75 76 77 78 'name': "thanlon", 79 'age': 24, 80 'gender': '男', 81 }, 82 2: { 83 84 85 86 'name': "kiku", 87 'age': 26, 88 'gender': '女', 89 }, 90 } 91 # token = request._request.GET.get('token') 92 ret = { 93 94 95 'code': 1000, "msg": None, 'data': None} 96 try: 97 ret['data'] = order_dict 98 except Exception as e: 99 pass 100 return JsonResponse(ret)
流程概述:
1- dispatch 2 - 封装request 3 - 获取定义的认证类(全局或者局部),通过列表生成式创建对象 4 - initial 5 - perform_authentication 6 request.user 7 内部循环认证类执行authenticate方法
3.3 全局配置认证
如果我们不使用自己的认证类,默认使用Django REST framework的认证类,路径在配置文件中。源码中有体现:


加下来可以这样来配置全局的认证类。在app目录下创建用于存放认证类的 auth,py 文件(认证的类不要写在views中,否则可能引用出现问题),然后在 settings.py 的认证配置项指向这个文件:
settings.py:
1REST_FRAMEWORK = { 2 3 4 5 'DEFAULT_AUTHENTICATION_CLASSES': ['app.auth.FirstAuthenticate', 'app.auth.Authenticate', ] 6}
auth.py:
1from rest_framework.views import exceptions 2from .models import UserToken 3 4class FirstAuthenticate: 5 def authenticate(self, request): 6 pass 7 8 def authenticate_header(self, request): 9 pass 10 11 12class Authenticate: 13 def authenticate(self, request): 14 token = request._request.GET.get('token') 15 print(token) 16 token_obj = UserToken.objects.filter(token=token).first() 17 if not token_obj: 18 raise exceptions.AuthenticationFailed('用户认证失败!') 19 # 在rest framework内部会将整个两个字段赋值给request,共后续操作使用 20 return (token_obj.user, token_obj) # (request.name,request.auth) 21 22 def authenticate_header(self, request): 23 pass
这样配置之后全部请求的方法都需要认证,但是有些是需要认证的,比如登录的方法,也需要认证:

只需要在类中把authentication_classes设置为空列表即可:
1class AuthView(APIView): 2 authentication_classes = [] 3 4 def post(self, request, *args, **kwargs): 5 print(md5('thanlon')) 6 ret = { 7 8 9 'code': 1000, 'msg': None} 10 try: 11 # 需要以form-data的方式提交 12 name = request._request.POST.get('name') 13 pwd = request._request.POST.get('pwd') 14 instance = User.objects.filter(name=name, pwd=pwd).first() # User object (1), 15 print(type(instance)) # <class 'app.models.User'>,加不加all()结果一样 16 print(instance) # User object (1),加不加all()结果一样 17 if not instance: 18 ret['code'] = 1001 19 ret['msg'] = '用户名或密码错误' 20 else: 21 token = md5(name=name) 22 UserToken.objects.update_or_create(user=instance, defaults={ 23 24 25 'token': token}) 26 ret['token'] = token 27 except Exception as e: 28 ret['code'] = 1001 29 ret['msg'] = '请求异常' 30 return JsonResponse(ret)
正常访问,不需要认证:

3.4 匿名用户配置
可以根据源码,来配置匿名用户:


settings.py:
1REST_FRAMEWORK = { 2 3 4 5 # 'DEFAULT_AUTHENTICATION_CLASSES': ['app.auth.FirstAuthenticate', 'app.auth.Authenticate', ] 6 'DEFAULT_AUTHENTICATION_CLASSES': ['app.auth.FirstAuthenticate', ], # FirstAuthenticate中什么也没有做 7 # 'UNAUTHENTICATED_USER': lambda x: '匿名用户', 8 'UNAUTHENTICATED_USER': None, # request.user = None,默认是AnonymousUser 9 'UNAUTHENTICATED_TOKEN': None # request.auth = None 10}
3.5 内置基本认证
Django REST framework中内置了很多内部认证类,

导入这些认证类的方式:
from rest_framework.authentication import BaseAuthentication,BasicAuthentication, SessionAuthentication,TokenAuthentication, RemoteUserAuthentication
BaseAuthentication类有两种方法,所有的认证类必须继承这个类:
1class BaseAuthentication: 2 """ 3 All authentication classes should extend BaseAuthentication. 4 """ 5 6 def authenticate(self, request): 7 """ 8 Authenticate the request and return a two-tuple of (user, token). 9 自定义认证操作的方法 10 """ 11 raise NotImplementedError(".authenticate() must be overridden.") 12 13 def authenticate_header(self, request): 14 """ 15 Return a string to be used as the value of the `WWW-Authenticate` 16 header in a `401 Unauthenticated` response, or `None` if the 17 authentication scheme should return `403 Permission Denied` responses. 18 认证失败之后给浏览器返回的响应头, 19 """ 20 pass
自定义认证类的时候,必须继承BaseAuthentication,其它的认证类中BasicAuthentication是浏览器对用户名和密码进行base64加密,
HTTP_AUTHORIZATION:basic base64(用户名和密码)
然后放到请求头里面发送给服务端。服务端接收数据后进行处理,之后做一系列的校验:

剩下的认证则是基于Django的session和token等实现的认证。
4. DRF权限
4.1 权限的基本使用
对于不同的视图应该有不同的访问权限,下面是权限的基本使用:
permission.py:
1class MyPermission1: 2 def has_permission(self, request, view): 3 # 超级用户可以访问 4 if request.user.user_type != 3: 5 return False 6 return True 7 8 9class MyPermission2: 10 def has_permission(self, request, view): 11 # 普通用户可以访问,超级用户不可以访问 12 if request.user.user_type == 3: 13 return False 14 return True
views.py:
1from rest_framework.views import APIView 2from .models import User, UserToken 3from django.http import JsonResponse 4from utils.md5 import md5 5from django.http import HttpResponse 6from app import permission 7 8class AuthView(APIView): 9 authentication_classes = [] 10 11 def post(self, request, *args, **kwargs): 12 ret = { 13 14 15 'code': 1000, 'msg': None} 16 try: 17 # 需要以form-data的方式提交 18 name = request._request.POST.get('name') 19 pwd = request._request.POST.get('pwd') 20 instance = User.objects.filter(name=name, pwd=pwd).first() # User object (1), 21 print(type(instance)) # <class 'app.models.User'>,加不加all()结果一样 22 print(instance) # User object (1),加不加all()结果一样 23 if not instance: 24 ret['code'] = 1001 25 ret['msg'] = '用户名或密码错误' 26 else: 27 token = md5(name=name) 28 UserToken.objects.update_or_create(user=instance, defaults={ 29 30 31 'token': token}) 32 ret['token'] = token 33 except Exception as e: 34 ret['code'] = 1001 35 ret['msg'] = '请求异常' 36 return JsonResponse(ret) 37 38class OrderView(APIView): 39 # 需要认证,使用自定义的Authenticate类来认证,已经在全局中做了认证 40 # authentication_classes = [FirstAuthenticate, Authenticate, ] 41 permission_classes = [permission.MyPermission2, ] 42 43 def get(self, request, *args, **kwargs): 44 # request.user 45 # request.auth 46 print(request.user) # User object (1) 47 print(request.auth) # print(request.auth)#User object (1) 48 """ 49 权限: 50 if request.user.user_type != 3: 51 return HttpResponse('无权访问') 52 """ 53 self.dispatch 54 order_dict = { 55 56 57 58 1: { 59 60 61 62 'name': "thanlon", 63 'age': 24, 64 'gender': '男', 65 }, 66 2: { 67 68 69 70 'name': "kiku", 71 'age': 26, 72 'gender': '女', 73 }, 74 } 75 # token = request._request.GET.get('token') 76 ret = { 77 78 79 'code': 1000, "msg": None, 'data': None} 80 try: 81 ret['data'] = order_dict 82 except Exception as e: 83 pass 84 return JsonResponse(ret)
使用权限MyPermission1,普通用访问拒绝:

使用MyPermission2,普通用户正常访问:

4.2 权限源码流程
通过源码熟悉权限的流程:









可以在自定的权限类中自定义权限拒绝的message:
1class MyPermission1: 2 message = '必须是超级用户才可以访问' 3 def has_permission(self, request, view): 4 # 超级用户可以访问 5 if request.user.user_type != 3: 6 return False 7 return True 8 9class MyPermission2: 10 def has_permission(self, request, view): 11 # 普通用户可以访问,超级用户不可以访问 12 if request.user.user_type == 3: 13 return False 14 return True

4.3 全局权限配置
根据权限的流程,可以对权限进行全局的配置:
settings.py:
1REST_FRAMEWORK = { 2 3 4 5 'DEFAULT_PERMISSION_CLASSES': ['app.permission.MyPermission1', ] # 所有的视图对应的方法都被加上这样的权限 6}
permission.py:
1class MyPermission1: 2 message = '必须是超级用户才可以访问' 3 def has_permission(self, request, view): 4 # 超级用户可以访问 5 if request.user.user_type != 3: 6 return False 7 return True 8 9class MyPermission2: 10 def has_permission(self, request, view): 11 # 普通用户可以访问,超级用户不可以访问 12 if request.user.user_type == 3: 13 return False 14 return True
源码流程概述:
11. self.dispatch 22. def dispatch(self, request, *args, **kwargs) 33. self.initial(request, *args, **kwargs) 44. self.check_permissions(request) 55. def get_permissions(self) 66. permission.has_permission(request, self)
4.4 内置权限类
Django REST framework内置了一些权限类:

按照代码规范,我们自己写的权限类应该继承这个BasePermission权限类:
1from rest_framework.permissions import BasePermission 2 3class MyPermission1(BasePermission): 4 def has_permission(self, request, view): 5 return True 6 7class MyPermission2(BasePermission): 8 def has_permission(self, request, view): 9 return True
这里的权限类基本上都是基于Django来做的,我们一般不使用这些类,而是自己定制。
5. 访问频率控制/节流
5.1 访问频率控制基本实现
可以根据 ip地址 来对用户访问频率进行限制,所以我们可以自定这样的访问频率控制的类:
还可以通过具有唯一标识意义的用户名或者用户ID等!
throttle.py:
1import time 2 3VISIT_RECORD = { 4 5 6 } 7 8 9class VisitThrottle: 10 def allow_request(self, request, view): 11 """ 12 设定10s之内只能访问10次 13 :param request: 14 :param view: 15 :return: True or False 16 返回值为True表示可以访问;返回值为False或None表示访问频率太高被限制 17 """ 18 # 获取用户的ip地址,当前request(封装)中有的就取当前的request,如果没有就到_request中取 19 remote_addr = request._request.META.get('REMOTE_ADDR') 20 print(remote_addr) # 127.0.0.1 21 ctime = time.time() 22 if remote_addr not in VISIT_RECORD: 23 VISIT_RECORD[remote_addr] = [ctime] 24 return True 25 history = VISIT_RECORD.get(remote_addr) 26 while history and history[-1] < ctime - 10: 27 history.pop(-1) 28 if len(history) < 10: 29 history.insert(0, ctime) 30 return True 31 return False # 写不写都可以,如果执行到这里说明不可访问的,返回False或None都可以表示不可以访问 32 33 def wait(self, *args, **kwargs): 34 pass
views.py:
1from rest_framework.views import APIView 2from .models import User, UserToken 3from django.http import JsonResponse 4from utils.md5 import md5 5from app.throttle import VisitThrottle 6 7 8class AuthView(APIView): 9 authentication_classes = [] 10 permission_classes = [] 11 throttle_classes = [VisitThrottle, ] 12 13 def post(self, request, *args, **kwargs): 14 ret = { 15 16 17 'code': 1000, 'msg': None} 18 try: 19 # 需要以form-data的方式提交 20 name = request._request.POST.get('name') 21 pwd = request._request.POST.get('pwd') 22 instance = User.objects.filter(name=name, pwd=pwd).first() # User object (1), 23 print(type(instance)) # <class 'app.models.User'>,加不加all()结果一样 24 print(instance) # User object (1),加不加all()结果一样 25 if not instance: 26 ret['code'] = 1001 27 ret['msg'] = '用户名或密码错误' 28 else: 29 token = md5(name=name) 30 UserToken.objects.update_or_create(user=instance, defaults={ 31 32 33 'token': token}) 34 ret['token'] = token 35 except Exception as e: 36 ret['code'] = 1001 37 ret['msg'] = '请求异常' 38 return JsonResponse(ret)
10s内只能发送10次请求,超出10次请求,则会返回。请求被限制:

当请求被拒绝后,系统还可以返回 距离下次可以请求的时间,可以通过wait方法来设置:
throttle.py:
1import time 2# 可以放到缓存中,默认Django REST framework中就是放在缓存中 3VISIT_RECORD = { 4 5 6 } 7 8 9class VisitThrottle: 10 def __init__(self): 11 self.history = None 12 self.ctime = None 13 14 def allow_request(self, request, view): 15 """ 16 设定10s之内只能访问10次 17 :param request: 18 :param view: 19 :return: True or False 20 返回值为True表示可以访问;返回值为False或None表示访问频率太高被限制 21 """ 22 # 获取用户的ip地址,当前request(封装)中有的就取当前的request,如果没有就到_request中取 23 remote_addr = request._request.META.get('REMOTE_ADDR') 24 print(remote_addr) # 127.0.0.1 25 self.ctime = time.time() # 1593151531.1494734 26 print(self.ctime) 27 if remote_addr not in VISIT_RECORD: 28 VISIT_RECORD[remote_addr] = [self.ctime] 29 return True 30 self.history = VISIT_RECORD.get(remote_addr) 31 while self.history and self.history[-1] < self.ctime - 10: 32 self.history.pop(-1) 33 if len(self.history) < 10: 34 self.history.insert(0, self.ctime) 35 return True 36 return False # 写不写都可以,如果执行到这里说明不可访问的,返回False或None都可以表示不可以访问 37 38 def wait(self, *args, **kwargs): 39 """ 40 设置距离下次可以请求的时间 41 :param args: 42 :param kwargs: 43 :return:默认返回None,表示使用默认 44 """ 45 """ 46 当请求被拒绝,会执行wait方法 47 """ 48 ctime = time.time() 49 print(ctime) 50 print(self.history[-1]) 51 """ 52 return False 53 { 54 "detail": "Request was throttled. Expected available in 0 seconds." 55 } 56 """ 57 return 60 - (ctime - self.history[-1])

5.2 访问频率控制源码流程
请求过来先走dispatch方法:

执行当前self.initialize_request方法:

执行self.check_throttles方法:

执行self.get_throttles方法,遍历访问控制类对象列表:

通过列表生成式生成访问控制类对象列表:

默认的访问控制类是从配置文件中获取的,也是全局的访问控制类:


如果访问被拒绝,即执行allow_request方法返回False或者None,则执行throttle.wait()方法:


5.3 访问频率全局配置
同认证和权限一样在配置文件中进行配置:
settings.py:
1REST_FRAMEWORK = { 2 3 4 5 'DEFAULT_THROTTLE_CLASSES': ['app.throttle.VisitThrottle',] 6}
同样某些请求可以不使用访问频率控制:
1class AuthView(APIView): 2 authentication_classes = [] 3 permission_classes = [] 4 # 访问频率控制 5 throttle_classes = [] 6 7 def post(self, request, *args, **kwargs): 8 pass
5.4 内置访问频率控制类
Django REST framewor中内置了五个访问频率控制类,所有的访问权限控制类应该继承BaseThrottle并重写其中的allow_request(self, request, view)方法:


内置的访问控制类SimpleRateThrottle其实帮助我们实现了基于IP的访问权限控制,我们看它内部是怎么实现的:


所以之前自定义的类实现的功能完全可以继承SimpleRateThrottle来实现,首先自定义类继承SimpleRateThrottle,并设置scope:
1from rest_framework.throttling import SimpleRateThrottle 2 3class VisitThrottle(SimpleRateThrottle): 4 # scope被当作key使用的,根据它到配置文件中取值 5 scope = 'erics'
在配置文件中添加DEFAULT_THROTTLE_RATES:
1REST_FRAMEWORK = { 2 3 4 5 'DEFAULT_THROTTLE_RATES': { 6 7 8 9 # 每分钟访问3次 10 'erics': '3/m' 11 } 12}
SimpleRateThrottle类中的逻辑是首先根据scope从配置文件中获取访问频率配置 3/m:

然后执行将获取到的rate也就是scope传入parse_rate函数进行解析:


__init__构造函数执行完成之后,执行allow_request方法:

获取key,把ip当作key:
1class VisitThrottle(SimpleRateThrottle): 2 # scope被当作key使用的,根据它到配置文件中取值 3 scope = 'erics' 4 5 def get_cache_key(self, request, view): 6 """获取key""" 7 return self.get_ident()
接下来到缓存获取所有记录:



之前自定义的时间,wait方法也已经做好,我们不需要去写:

最终我们只需要几行代码就可以完成对匿名用户的访问频率的控制:
1from rest_framework.throttling import SimpleRateThrottle 2 3class VisitThrottle(SimpleRateThrottle): 4 # scope被当作key使用的,根据它到配置文件中取值 5 scope = 'erics' 6 7 def get_cache_key(self, request, view): 8 """获取key""" 9 return self.get_ident(request) 10 11 12REST_FRAMEWORK = { 13 14 15 16 'DEFAULT_THROTTLE_CLASSES': ['app.throttle.VisitThrottle', ], 17 'DEFAULT_THROTTLE_RATES': { 18 19 20 21 # 每分钟访问5次,/右边只要首字母是m就可以了 22 'erics': '5/m' 23 } 24}
匿名用户每分钟只能访问5次:

上面是对匿名用户的访问登录控制,也可以对登录用户做访问频率控制。只需要根据用户名来做频率控制条件:
1from rest_framework.throttling import SimpleRateThrottle 2 3class UserVisitThrottle(SimpleRateThrottle): 4 # scope被当作key使用的,根据它到配置文件中取值 5 scope = 'user_erics' 6 7 def get_cache_key(self, request, view): 8 """获取key""" 9 print(request) # <rest_framework.request.Request object at 0x7f155c6b86d0> 10 # 用户认证成功之后就会有request.user 11 print(request.user) # User object (1) 12 print(request.user.name) # thanlon 13 return request.user.name 14 15 16REST_FRAMEWORK = { 17 18 19 20 # 'DEFAULT_THROTTLE_CLASSES': ['app.throttle.VisitThrottle', ], 21 # 登录的用户使用根据用户名来做频率限制,匿名用户使用IP来做频率限制。这里全局配置了登录用户的,匿名用户需要可以单独设置通过ip来控制 22 'DEFAULT_THROTTLE_CLASSES': ['app.throttle.UserVisitThrottle', ], 23 'DEFAULT_THROTTLE_RATES': { 24 25 26 27 # 每分钟访问5次,/右边只要首字母是m就可以了 28 'erics': '5/m', 29 'user_erics': '10/m' 30 } 31}
登录的用户每分钟只能访问10次:

6. DRF版本
6.1 GET传参获取版本
RESTful规范中规定版本可以放到URL上,如 http://127.0.0.1:8000/api/users/?version=v2 通过GET进行传参。自定义版本实现:
1from django.shortcuts import render, HttpResponse 2from rest_framework.views import APIView 3from rest_framework.request import Request 4from rest_framework.versioning import BaseVersioning 5 6class ParamVersion: 7 def determine_version(self, request, *args, **kwargs): 8 # version = request._request.GET.get('version') 9 version = request.query_params.get('version') 10 return version 11 12class UsersView(APIView): 13 versioning_class = ParamVersion # request.version就会有值 14 15 def get(self, request, *args, **kwargs): 16 # request是Request的对象,如果封装的request没有回通过getattr方法到原生的Django的request中找 17 # version = request._request.GET.get('version') 18 # version = request.query_params.get('version') 19 version = request.version 20 # print(request.version) 21 return HttpResponse('%s' % version)
如果使用内置的版本类,实际上我们 不需要再自定义获取版本的类,直接在视图类方法中通过 request.version 获取就可以,因为内置已经帮我们实现了。借助内置类,我们可以少写很多代码:
1from django.shortcuts import render, HttpResponse 2from rest_framework.views import APIView 3from rest_framework.versioning import QueryParameterVersioning 4 5class UsersView(APIView): 6 versioning_class = QueryParameterVersioning 7 8 def get(self, request, *args, **kwargs): 9 version = request.version 10 return HttpResponse('%s' % version)
可以在配置文件中做默认的配置:
1REST_FRAMEWORK = { 2 3 4 5 'DEFAULT_VERSION': 'v1', # 不传参或者version写错了则获取到的版本号是v1 6 'ALLOWED_VERSIONS': ['v1', 'v2'], # 版本号必须是v1或者v2,否则报错如:http://xxx/api/users/?version=v3 7 'VERSION_PARAM': 'version', 8}

6.2 URL路径获取版本
通过GET进行传参用的相对少一些,比较多的是在URL路径上传参,即 ``。只需要修改URL,然后在视图中引入 URLPathVersioning 内置版本类:
api/urls.py:
1from django.urls import path, re_path 2from . import views 3 4urlpatterns = [ 5 # path('/users/', views.UsersView.as_view()), 6 re_path('(?P<version>[v1,v2]+)/users/', views.UsersView.as_view()), 7]
版本是一次性配置,可以把版本的类加入到配置文件中:
settings.py:
1REST_FRAMEWORK = { 2 3 4 5 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', # 使用URL路径,下面的参数可以不配置,只要URL正则匹配就可以 6 'DEFAULT_VERSION': 'v1', # 不传参或者version写错了则获取到的版本号是v1 7 'ALLOWED_VERSIONS': ['v1', 'v2'], # 版本号必须是v1或者v2,否则报错如:http://xxx/api/users/?version=v3 8 'VERSION_PARAM': 'version', 9}
GET传参适用的内置类QueryParameterVersioning也可以放到配置文件中,这样降低了类和类之间的耦合度。
views.py:
1from django.shortcuts import render, HttpResponse 2from rest_framework.views import APIView 3 4class UsersView(APIView): 5 6 def get(self, request, *args, **kwargs): 7 version = request.version 8 return HttpResponse('%s' % version)

6.3 内置版本类源码流程







通过reserve反向生成url:
1from django.urls import path, re_path 2from . import views 3 4urlpatterns = [ 5 # path('users/', views.UsersView.as_view()), 6 re_path('(?P<version>[v1,v2]+)/users/', views.UsersView.as_view(), name='user'), 7] 8 9 10from django.shortcuts import HttpResponse 11from rest_framework.views import APIView 12 13 14class UsersView(APIView): 15 16 def get(self, request, *args, **kwargs): 17 # 获取版本 18 version = request.version 19 # 获取处理版本的对象 20 versioning_scheme = request.versioning_scheme 21 # 所有的版本类对象中有reverse方法用来反向生成url的,版本自动生成。不需要加上version参数,request=request表示在request自动设置上当前的版本 22 url = request.versioning_scheme.reverse(viewname='user', request=request) 23 self.dispatch 24 return HttpResponse('%s' % (url,))

也可以适用Django的反向生成url,只不过要手动加上版本参数:


6.4 内置版本类
Django REST framework内置了5个版本类,自定义版本类需要继承BaseVersioning,但是一般我们不需要自定义,内置的已经够我们使用的。QueryParameterVersioning是通过在URL中使用GET传参;URLPathVersioning是URL路径;HostNameVersioning是基于子域名做的;NamespaceVersioning是基于namespace,但是不常用;AcceptHeaderVersioning是基于请求头。

放到cookie中也是可以的,只要后端能正常接收。
7. DRF解析器
7.1 Django中数据的解析
Django接收请求和请求相关的数据时对请求头和数据格式有一定的要求,首先看下对请求头要求。只有请求头中有 application/x-www-form-urlencoded,request.POST中才可能有值(去request.body中解析数据):
urls.py:
1from django.urls import path, re_path 2from . import views 3 4urlpatterns = [ 5 re_path('(?P<version>[v1,v2]+)/django_request/', views.DjangoRequest.as_view()), 6]
views.py:
1from django.shortcuts import HttpResponse 2from rest_framework.views import APIView 3 4class DjangoRequest(APIView): 5 def post(self, request, *args, **kwargs): 6 from django.core.handlers.wsgi import WSGIRequest 7 print(type(request._request)) # <class 'django.core.handlers.wsgi.WSGIRequest'> 8 return HttpResponse()
可以看下源码中是如何做的:



上面说过即便带着这个Content-Type也不一定有值,数据解析(转换)时对数据格式也是有要求的,数据格式需要是 ?name=thanlon&age=23 这种格式。
之后遇到request.POST没有值,可以考虑这两个因素。
Form表单提交默认的Content-Type就是application/x-www-form-urlencoded,数据格式也是?name=thanlon&age=23。如果是Ajax提交:
1$.ajax({ 2 3 4 5 url:..., 6 type:POST, 7 data:{ 8 9 10 'name':'thanlon','age':23}, // 内部也会转换成?name=thanlon&age=23,再带上Content-Type:application/x-www-form-urlencoded提交过去 11})
Ajax可以定制请求头,这种情况下request.body中有值,但是request.post中没有值:
1$.ajax({ 2 3 4 5 url:..., 6 headers:{ 7 8 9 'Content-Type':'application/json'}, 10 type:POST, 11 data:{ 12 13 14 'name':'thanlon','age':23}, 15})
Ajax将数据转换为JSON字符串传到后台,这种情况下request.body中有值,但是request.post中还是没有值:
1$.ajax({ 2 3 4 5 url:..., 6 headers:{ 7 8 9 'Content-Type':'application/json'}, 10 type:post, 11 data:JSON.stringfy({ 12 13 14 'name':'thanlon','age':23}), // {'name':'thanlon','age':23} 15})
这种情况下request.body中是有值,可以使用 json.loads(request.body) 拿到json数据。当然,需要先把字节类型转换为字符串类型。
7.2 内置解析类的使用
常用的内置解析类有 JSONParser和FormParser,JSONParser类可以解析请求头中Content-Type是application/json的头的数据,FormParser类可以解析请求中Content-Type是application/x-www-form-urlencoded的头的数据。Content-Type是application/json时:
urls.py:
1from django.urls import re_path 2from . import views 3 4urlpatterns = [ 5 re_path('(?P<version>[v1,v2]+)/parser/', views.ParserView.as_view()), 6]
views.py:
1from django.shortcuts import HttpResponse 2from rest_framework.views import APIView 3from rest_framework.parsers import JSONParser 4 5class ParserView(APIView): 6 parser_classes = [JSONParser, ] 7 8 def post(self, request, *args, **kwargs): 9 """ 10 允许用户发送JSON格式数据,请求头中Content-Type是application/json,数据是{'name': 'erics'} 11 :param request: 12 :param args: 13 :param kwargs: 14 :return: 15 """ 16 # request.data会触发解析类进行数据的解析 17 data = request.data 18 print(type(data), data) # <class 'dict'> {'name': 'erics'} 19 return HttpResponse(f'{data}')

Content-Type是application/x-www-form-urlencoded时:
1from django.shortcuts import HttpResponse 2from rest_framework.views import APIView 3from rest_framework.parsers import JSONParser, FormParser 4 5class ParserView(APIView): 6 parser_classes = [JSONParser, FormParser,] 7 8 def post(self, request, *args, **kwargs): 9 data = request.data 10 """ 11 1、获取用户请求 12 2、获取用户请求体 13 3、获取用户请求头,需要与parser_classes = [JSONParser, FormParser]中支持的请求头进行比较 14 4、JSONParser、FormParser对象解析请求体 15 5、解析的结果放到request.data中 16 """ 17 print(type(data), data) # <class 'django.http.request.QueryDict'> <QueryDict: {'name': ['erics']}> 18 return HttpResponse(f'{data}')

Content-Type是multipart/form-data时,需要用到MultiPartParser解析类:

如果都不可以解析请求的数据:

配置多个请求类时,请求头中的Content-Type与哪一个解析类匹配就使用哪个解析。
全局配置只需要在配置文件中添加解析类的路径即可:
1REST_FRAMEWORK = { 2 3 4 5 'DEFAULT_PARSER_CLASSES': 6 [ 7 'rest_framework.parsers.JSONParser', 8 'rest_framework.parsers.FormParser', 9 'rest_framework.parsers.MultiPartParser', 10 ] 11}
7.3 内置解析类
Django REST framework内置的解析类实际上有4种,都是根据请求头中Content-Type值的不同来对数据做不同的解析:

7.4 解析源码流程














8. DRF序列化
8.1 序列化基本使用
数据准备:
models.py:
1from django.db import models 2 3 4class UserInfo(models.Model): 5 user_type_choices = ( 6 (1, '普通用户'), 7 (2, 'VIP用户'), 8 (3, 'SVIP用户'), 9 ) 10 user_type = models.IntegerField(choices=user_type_choices) 11 name = models.CharField(max_length=32, unique=True) 12 pwd = models.CharField(max_length=64) 13 user_group = models.ForeignKey('UserGroup', on_delete=models.CASCADE) 14 role = models.ManyToManyField('Role') 15 16 17class Token(models.Model): 18 user_info = models.OneToOneField(to='UserInfo', on_delete=models.CASCADE) 19 token = models.CharField(max_length=64) 20 21 22class UserGroup(models.Model): 23 title = models.CharField(max_length=32) 24 25 26class Role(models.Model): 27 title = models.CharField(max_length=32)
生成的数据表:

在角色表中添加如下数据:

原先使用 json.dumps 方法对从数据库中获取到的数据进行序列化:
urls.py:
1from django.urls import path, re_path 2from . import views 3 4urlpatterns = [ 5 re_path('(?P<version>[v1,v2]+)/roles/', views.RolesView.as_view()), 6]
views.py:
1from django.shortcuts import HttpResponse 2from rest_framework.views import APIView 3from . import models 4import json 5 6class RolesView(APIView): 7 def get(self, request, *args, **kwargs): 8 roles = models.Role.objects.all() 9 print(roles) # <QuerySet [<Role: Role object (1)>, <Role: Role object (2)>]> 10 roles = models.Role.objects.all().values('id', 'title') 11 print(roles) # <QuerySet [{'id': 1, 'title': '管理员'}, {'id': 2, 'title': '超级管理员'}]> 12 roles = list(roles) # [<Role: Role object (1)>, <Role: Role object (2)>] 13 print(roles) # [{'id': 1, 'title': '管理员'}, {'id': 2, 'title': '超级管理员'}] 14 ret = json.dumps(roles, ensure_ascii=False) 15 return HttpResponse(ret)

现在我们可以使用Django REST framework中的序列化来对从数据库中的获取的数据进行序列化:
views.py:
1from django.shortcuts import HttpResponse 2from rest_framework.views import APIView 3from . import models 4import json 5from rest_framework import serializers 6 7class MySerializers(serializers.Serializer): 8 """ 9 自定义序列化类 10 """ 11 id = serializers.IntegerField() 12 title = serializers.CharField() 13 14class RolesView(APIView): 15 def get(self, request, *args, **kwargs): 16 roles = models.Role.objects.all() 17 # 因为对象不止一个,序列化[obj,obj,obj]这种,要使用many=True 18 serializers = MySerializers(instance=roles, many=True) 19 print(serializers) 20 """ 21 MySerializers(instance=<QuerySet [<Role: Role object (1)>, <Role: Role object (2)>]>, many=True): 22 id = IntegerField() 23 title = CharField() 24 """ 25 # serializers.data是已经转换完成的结果 26 print(serializers.data) # [OrderedDict([('id', 1), ('title', '管理员')]), OrderedDict([('id', 2), ('title', '超级管理员')])] 27 28 ret = json.dumps(serializers.data, ensure_ascii=False) 29 return HttpResponse(ret)

对于从数据库中获取一条数据,如果适用序列化:
views.py:
1from django.shortcuts import HttpResponse 2from rest_framework.views import APIView 3from . import models 4import json 5from rest_framework import serializers 6 7 8class MySerializers(serializers.Serializer): 9 """ 10 自定义序列化类 11 """ 12 id = serializers.IntegerField() 13 title = serializers.CharField() 14 15 16class RolesView(APIView): 17 def get(self, request, *args, **kwargs): 18 roles = models.Role.objects.first() 19 # 因为就一个对象,序列化obj,要使用many=False 20 serializers = MySerializers(instance=roles, many=False) # 默认也是False 21 print(serializers) 22 """ 23 MySerializers(instance=<Role: Role object (1)>): 24 id = IntegerField() 25 title = CharField() 26 """ 27 print(serializers.data) # {'id': 1, 'title': '管理员'} 28 # serializers.data是已经转换完成的结果 29 ret = json.dumps(serializers.data, ensure_ascii=False) 30 return HttpResponse(ret)

8.2 自定义字段
Django REST framework的序列化可以允许我们自定义字段,从数据库中获取字段值也特别方便:
1from django.urls import path, re_path 2from . import views 3 4urlpatterns = [ 5 re_path('(?P<version>[v1,v2]+)/userinfo/', views.UserinfoView.as_view()), 6] 7 8 9from django.shortcuts import HttpResponse 10from rest_framework.views import APIView 11from . import models 12import json 13from rest_framework import serializers 14 15class UserinfoSerializer(serializers.Serializer): 16 username = serializers.CharField() 17 pwd = serializers.CharField() 18 user_type_id = serializers.CharField(source='user_type') # source对应数据库中字段,row.user_type 19 user_type_title = serializers.CharField(source='get_user_type_display') # row.get_user_type_display() 20 group_title = serializers.CharField(source='user_group.title') 21 rls = serializers.SerializerMethodField() # 自定义展示 22 23 def get_rls(self, row): 24 row_obj_list = row.role.all() 25 ret = [] 26 for item in row_obj_list: 27 ret.append({ 28 29 30 'id': item.id, 'title': item.title}) 31 return ret 32 33 34class UserinfoView(APIView): 35 def get(self, request, *args, **kwargs): 36 userinfo = models.UserInfo.objects.all() 37 userinfo_serializer = UserinfoSerializer(userinfo, many=True) 38 ret = json.dumps(userinfo_serializer.data, ensure_ascii=False) 39 return HttpResponse(f"{ret}")

8.3 ModelSerializer
ModelSerializer类继承了Serializer,内部做了一些操作可以自动生成所有的字段:
1class UserinfoSerializer(serializers.ModelSerializer): 2 class Meta: 3 model = models.UserInfo 4 fields = "__all__" 5 6class UserinfoView(APIView): 7 def get(self, request, *args, **kwargs): 8 userinfo = models.UserInfo.objects.all() 9 userinfo_serializer = UserinfoSerializer(userinfo, many=True) 10 ret = json.dumps(userinfo_serializer.data, ensure_ascii=False) 11 return HttpResponse(f"{ret}")

简单的可以直接用数据库的字段适用ModelSerializer生成,复杂的可以自定义:
1class UserinfoSerializer(serializers.ModelSerializer): 2 user_type_title = serializers.CharField(source='get_user_type_display') 3 group_title = serializers.CharField(source='user_group.title') 4 rls = serializers.SerializerMethodField() 5 6 class Meta: 7 model = models.UserInfo 8 # fields = "__all__" 9 fields = ['id', 'username', 'pwd', 'rls', 'user_type_title', 'group_title',] 10 11 def get_rls(self, row): 12 row_obj_list = row.role.all() 13 ret = [] 14 for item in row_obj_list: 15 ret.append({ 16 17 18 'id': item.id, 'title': item.title}) 19 return ret 20 21 22class UserinfoView(APIView): 23 def get(self, request, *args, **kwargs): 24 userinfo = models.UserInfo.objects.all() 25 userinfo_serializer = UserinfoSerializer(userinfo, many=True) 26 ret = json.dumps(userinfo_serializer.data, ensure_ascii=False) 27 return HttpResponse(f"{ret}")

生成字段的时候可以额外加一些参数,可以不用再定义group_title:
1class UserinfoSerializer(serializers.ModelSerializer): 2 user_type_title = serializers.CharField(source='get_user_type_display') 3 # group_title = serializers.CharField(source='user_group.title') 4 rls = serializers.SerializerMethodField() 5 6 class Meta: 7 model = models.UserInfo 8 # fields = "__all__" 9 fields = ['id', 'username', 'pwd', 'rls', 'user_type_title', ] 10 extra_kwargs = { 11 12 13 'group_title': { 14 15 16 'source': 'user_group.title'}} # 和上面自定义一样 17 18 def get_rls(self, row): 19 row_obj_list = row.role.all() 20 ret = [] 21 for item in row_obj_list: 22 ret.append({ 23 24 25 'id': item.id, 'title': item.title}) 26 return ret 27 28class UserinfoView(APIView): 29 def get(self, request, *args, **kwargs): 30 userinfo = models.UserInfo.objects.all() 31 userinfo_serializer = UserinfoSerializer(userinfo, many=True) 32 ret = json.dumps(userinfo_serializer.data, ensure_ascii=False) 33 return HttpResponse(f"{ret}")

8.4 深度控制
只需要在Meta中设置 depth 字段就可以 自动序列化连表 获取关联表中的字段:
1class UserinfoSerializer(serializers.ModelSerializer): 2 class Meta: 3 model = models.UserInfo 4 fields = "__all__" 5 """ 6 depth:0~10,0表示只获取UserInfo中的表中的字段的值 7 """ 8 depth = 1 9 10class UserinfoView(APIView): 11 def get(self, request, *args, **kwargs): 12 userinfo = models.UserInfo.objects.all() 13 userinfo_serializer = UserinfoSerializer(userinfo, many=True) 14 ret = json.dumps(userinfo_serializer.data, ensure_ascii=False) 15 return HttpResponse(f"{ret}")

1class UserinfoSerializer(serializers.ModelSerializer): 2 class Meta: 3 model = models.UserInfo 4 fields = ['id', 'username', 'pwd', 'user_type', 'user_group', 'role'] 5 depth = 1

8.5 生成链接
Django REST framework中的序列化也可以帮助我们生成超链接:
urls.py:
1from django.urls import path, re_path 2from . import views 3 4urlpatterns = [ 5 re_path('(?P<version>[v1,v2]+)/userinfo/', views.UserinfoView.as_view()), 6 re_path('(?P<version>[v1,v2]+)/group/(?P<pk>\d+)', views.GroupView.as_view(), name='gp'), 7] 8 9 10from django.shortcuts import HttpResponse 11from rest_framework.views import APIView 12from . import models 13import json 14from rest_framework import serializers 15 16class UserinfoSerializer(serializers.ModelSerializer): 17 group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_url_kwarg='pk', 18 lookup_field='user_group_id') # pk和url上自定义的pk有关系 19 20 class Meta: 21 model = models.UserInfo 22 fields = ['id', 'username', 'pwd', 'user_type', 'user_group', 'role', 'group'] 23 depth = 1 24 25class UserinfoView(APIView): 26 def get(self, request, *args, **kwargs): 27 userinfo = models.UserInfo.objects.all() 28 userinfo_serializer = UserinfoSerializer(userinfo, many=True, context={ 29 30 31 'request': request}) 32 ret = json.dumps(userinfo_serializer.data, ensure_ascii=False) 33 return HttpResponse(f"{ret}") 34 35 36class GroupSerializer(serializers.ModelSerializer): 37 class Meta: 38 model = models.UserGroup 39 fields = '__all__' 40 41 42class GroupView(APIView): 43 def get(self, request, *args, **kwargs): 44 pk = kwargs.get('pk') # url传过来的pk对应/(?P<version>[v1,v2]+)/group/(?P<pk>\d+)/ 45 obj = models.UserGroup.objects.filter(pk=pk).first() 46 group_serializer = GroupSerializer(instance=obj, many=False) 47 ret = json.dumps(group_serializer.data, ensure_ascii=False) 48 return HttpResponse(f"{ret}")


8.6 请求数据校验
Django REST framework序列化还可以做 请求数据校验,简单的数据校验:
1class UserGroupSerializer(serializers.Serializer): 2 # title = serializers.CharField(error_messages={'required':'dsdsd'}) # error_messages暂时没作用 3 # 写需要校验的字段 4 title = serializers.CharField() 5 6 def validate_title(self, value): 7 from rest_framework import exceptions 8 raise exceptions.ValidationError('错误!') 9 return value 10 11class UserGroupView(APIView): 12 def post(self, request, *args, **kwargs): 13 print(request.data) # {'title': ''} 14 ser = UserGroupSerializer(data=request.data) 15 if not ser.is_valid(): 16 print( 17 ser.errors['title'][0]) # {'title': [ErrorDetail(string='This field may not be blank.', code='blank')]} 18 else: 19 print(ser.validated_data, 20 type(ser.validated_data)) # OrderedDict([('title', '3组')]) <class 'collections.OrderedDict'> 21 print(ser.validated_data['title']) # 3组 22 return HttpResponse()


8.7 自定义验证规则
简单的数据校验一般不能满足需求,很多情况下需要自定义验证规则校验数据:
1class UserGroupValidator: 2 def __init__(self, base): 3 self.base = str(base) 4 5 def __call__(self, value): 6 """数据一提交过来就会执行这个__call__方法""" 7 print('提交过来title的值', value) # value是提交过来的值 8 if not value.endswith(self.base): 9 message = '标题必须以%s结尾!' % self.base 10 raise serializers.ValidationError(message) 11 12 def set_context(self, serializer_field): 13 """执行验证之前调用,serializer_field是当前字段对象,这里用不到""" 14 pass 15 16 17class UserGroupSerializer(serializers.Serializer): 18 title = serializers.CharField(validators=[UserGroupValidator('组'), ]) 19 20 21class UserGroupView(APIView): 22 def post(self, request, *args, **kwargs): 23 # print(request.data) # {'title': '3'} 24 ser = UserGroupSerializer(data=request.data) 25 if not ser.is_valid(): 26 print( 27 ser.errors['title'][0]) # {'title': [ErrorDetail(string='This field may not be blank.', code='blank')]} 28 else: 29 print(ser.validated_data, 30 type(ser.validated_data)) # OrderedDict([('title', '3组')]) <class 'collections.OrderedDict'> 31 print(ser.validated_data['title']) # 3组 32 return HttpResponse()




自定义验证规则时,需要用到钩子函数,需要从 is_valid 方法开始找。
9. DRF分页
9.1 内置分页
① 查看第 n 页,每页显示 m 条数据。settings.py 中配置的是默认显示的数据条数。使用到的内置类:PageNumberPagination,
settings.py
1REST_FRAMEWORK = { 2 3 4 5 'PAGE_SIZE': 2, 6}
views.py:
1from rest_framework import serializers 2from api import models 3from rest_framework.response import Response 4from rest_framework.pagination import PageNumberPagination 5 6 7class PagerSerializer(serializers.ModelSerializer): 8 class Meta: 9 model = models.Role 10 fields = "__all__" 11 12 13class PagerView(APIView): 14 def get(self, request, *args, **kwargs): 15 # 获取所有数据 16 roles = models.Role.objects.all() 17 # 创建分页对象 18 pg = PageNumberPagination() 19 # 获取数据库中分页的数据 20 pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self) 21 """ 22 print(pager_roles) # [<Role: Role object (1)>, <Role: Role object (2)>] 23 """ 24 # 序列化数据 25 serializer = PagerSerializer(instance=pager_roles, many=True) 26 """ 27 ret = json.dumps(serializer.data) 28 return HttpResponse(ret) 29 """ 30 return Response(serializer.data)


② 第 n 页向前向后查 m 条数据,使用到的内置类:LimitOffsetPagination
settings.py
1REST_FRAMEWORK = { 2 3 4 5 'PAGE_SIZE': 2, 6} 7 8 9from rest_framework import serializers 10from api import models 11from rest_framework.response import Response 12from rest_framework.pagination import LimitOffsetPagination 13 14 15class PagerSerializer(serializers.ModelSerializer): 16 class Meta: 17 model = models.Role 18 fields = "__all__" 19 20 21class PagerView(APIView): 22 def get(self, request, *args, **kwargs): 23 # 获取所有数据 24 roles = models.Role.objects.all() 25 # 创建分页对象 26 pg = LimitOffsetPagination() 27 # 获取数据库中分页的数据 28 pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self) 29 """ 30 print(pager_roles) # [<Role: Role object (1)>, <Role: Role object (2)>] 31 """ 32 # 序列化数据 33 serializer = PagerSerializer(instance=pager_roles, many=True) 34 """ 35 ret = json.dumps(serializer.data) 36 return HttpResponse(ret) 37 """ 38 return Response(serializer.data)


③ 解决数据量比较大时,降低扫描的数据量。同时要对页码进行加密,防止因用户随意指定页码没有实现降低扫描数据量而提高查询效率的目的。使用到的内置类:CursorPagination,这部分需不能直接使用内置累,需要自定义类来实现! 下面会有介绍。
9.2 自定义分页
① 查看第 n 页,每页显示 m 条数据
settings.py
1REST_FRAMEWORK = { 2 3 4 5 'PAGE_SIZE': 2, 6}
views.py:
1from rest_framework import serializers 2from api import models 3from rest_framework.response import Response 4from rest_framework.pagination import PageNumberPagination 5 6 7class MyPageNumberPagination(PageNumberPagination): 8 page_size = 2 # 在这里设置可以覆盖settings.py中的配置 9 page_query_param = 'page' 10 page_size_query_param = 'size' 11 max_page_size = 3 12 13 14class PagerSerializer(serializers.ModelSerializer): 15 class Meta: 16 model = models.Role 17 fields = "__all__" 18 19 20class PagerView(APIView): 21 def get(self, request, *args, **kwargs): 22 # 获取所有数据 23 roles = models.Role.objects.all() 24 # 创建分页对象 25 pg = MyPageNumberPagination() 26 # 获取数据库中分页的数据 27 pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self) 28 """ 29 print(pager_roles) # [<Role: Role object (1)>, <Role: Role object (2)>] 30 """ 31 # 序列化数据 32 serializer = PagerSerializer(instance=pager_roles, many=True) 33 """ 34 ret = json.dumps(serializer.data) 35 return HttpResponse(ret) 36 return Response(serializer.data) 37 """ 38 return Response(serializer.data)

② 第 n 页向前向后查 m 条数据
settings.py
1REST_FRAMEWORK = { 2 3 4 5 'PAGE_SIZE': 2, 6}
views.py:
1from rest_framework import serializers 2from api import models 3from rest_framework.response import Response 4from rest_framework.pagination import LimitOffsetPagination 5 6class MyLimitOffsetPagination(LimitOffsetPagination): 7 limit_query_param = 'limit' 8 offset_query_param = 'offset' 9 max_limit = 2 10 11 12class PagerSerializer(serializers.ModelSerializer): 13 class Meta: 14 model = models.Role 15 fields = "__all__" 16 17 18class PagerView(APIView): 19 def get(self, request, *args, **kwargs): 20 # 获取所有数据 21 roles = models.Role.objects.all() 22 # 创建分页对象 23 pg = MyLimitOffsetPagination() 24 # 获取数据库中分页的数据 25 pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self) 26 """ 27 print(pager_roles) # [<Role: Role object (1)>, <Role: Role object (2)>] 28 """ 29 # 序列化数据 30 serializer = PagerSerializer(instance=pager_roles, many=True) 31 """ 32 ret = json.dumps(serializer.data) 33 return HttpResponse(ret) 34 return Response(serializer.data) 35 """ 36 return Response(serializer.data)

③ 降低扫描的数据量,同时对页码进行加密
settings.py
1REST_FRAMEWORK = { 2 3 4 5 'PAGE_SIZE': 2, 6}
views.py:
1from rest_framework import serializers 2from api import models 3from rest_framework.response import Response 4from rest_framework.pagination import CursorPagination 5 6 7class MyCursorPagination(CursorPagination): 8 cursor_query_param = 'cursor' 9 page_size = 2 10 ordering = 'id' # 倒序是-id 11 page_size_query_param = None 12 max_page_size = None 13 14 15class PagerSerializer(serializers.ModelSerializer): 16 class Meta: 17 model = models.Role 18 fields = "__all__" 19 20 21class PagerView(APIView): 22 def get(self, request, *args, **kwargs): 23 # 获取所有数据 24 roles = models.Role.objects.all() 25 # 创建分页对象 26 pg = MyCursorPagination() 27 # 获取数据库中分页的数据 28 pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self) 29 """ 30 print(pager_roles) # [<Role: Role object (1)>, <Role: Role object (2)>] 31 """ 32 # 序列化数据 33 serializer = PagerSerializer(instance=pager_roles, many=True) 34 """ 35 ret = json.dumps(serializer.data) 36 return HttpResponse(ret) 37 return Response(serializer.data) 38 return Response(serializer.data) 39 """ 40 return pg.get_paginated_response(serializer.data)


get_paginated_response方法可以返回更多的内容!
10. DRF视图
10.1 GenericAPIView
视图继承 GenericAPIView ,GenericAPIView 继承 APIView, APIView 继承


settings.py:
1REST_FRAMEWORK = { 2 3 4 5 'PAGE_SIZE': 2, 6}
urls.py:
1from django.urls import path, re_path 2from . import views 3 4urlpatterns = [ 5 re_path('(?P<version>[v1,v2]+)/testview/', views.TestView.as_view()), 6]
views.py:
1from rest_framework import serializers 2from rest_framework.generics import GenericAPIView 3from rest_framework.pagination import PageNumberPagination 4from rest_framework.response import Response 5 6 7class PagerSerializer(serializers.ModelSerializer): 8 class Meta: 9 model = models.Role 10 fields = "__all__" 11 12 13class TestView(GenericAPIView): 14 queryset = models.Role.objects.all() 15 pagination_class = PageNumberPagination 16 serializer_class = PagerSerializer 17 18 def get(self, request, *args, **kwargs): 19 # 获取数据 20 roles = self.get_queryset() # queryset: models.Role.objects.all() 21 # 分页 22 pager_roles = self.paginate_queryset(roles) 23 # 序列化 24 serializer = self.get_serializer(instance=pager_roles, many=True) 25 return Response(serializer.data)

一般用不到GenericAPIView!
10.2 GenericViewSet
视图继承 GenericViewSet,可以根据请求方法的不同把请求分发到不同的方法,比原来都分发到 get 然后判断有无 id 要好一些。 GenericViewSet 继承 ViewSetMixin 和 GenericAPIView。


ViewSetMixin只是重写了as_view方法!
settings.py:
1REST_FRAMEWORK = { 2 3 4 5 'PAGE_SIZE': 2, 6}
urls.py:
1from django.urls import path, re_path 2from . import views 3 4urlpatterns = [ 5 re_path('(?P<version>[v1,v2]+)/testview/', views.TestView.as_view({ 6 7 8 'get': 'list'})), 9]
views.py:
1from rest_framework import serializers 2from rest_framework.viewsets import GenericViewSet 3from rest_framework.pagination import PageNumberPagination 4from rest_framework.response import Response 5 6 7class PagerSerializer(serializers.ModelSerializer): 8 class Meta: 9 model = models.Role 10 fields = "__all__" 11 12 13class TestView(GenericViewSet): 14 queryset = models.Role.objects.all() 15 pagination_class = PageNumberPagination 16 serializer_class = PagerSerializer 17 18 def list(self, request, *args, **kwargs): 19 # 获取数据 20 roles = self.get_queryset() # queryset: models.Role.objects.all() 21 # 分页 22 pager_roles = self.paginate_queryset(roles) 23 # 序列化 24 serializer = self.get_serializer(instance=pager_roles, many=True) 25 return Response(serializer.data)

页码是从1开始的!
10.3 ModelViewSet
ModelViewSet 继承6个类:

settings.py:
1REST_FRAMEWORK = { 2 3 4 5 'PAGE_SIZE': 2, 6}
urls.py:
1from django.urls import path, re_path 2from . import views 3 4urlpatterns = [ 5 re_path('(?P<version>[v1,v2]+)/testview/', views.TestView.as_view({ 6 7 8 'get': 'list','post':'create'})), 9 re_path('(?P<version>[v1,v2]+)/testview/(?P<pk>\d+)', views.TestView.as_view( 10 { 11 12 13 'get':'retrieve','delete':'destroy','put':'update','patch':'partial_update'} 14 )), 15]
views.py:
1from rest_framework import serializers 2from rest_framework.viewsets import ModelViewSet 3from rest_framework.pagination import PageNumberPagination 4 5 6class PagerSerializer(serializers.ModelSerializer): 7 class Meta: 8 model = models.Role 9 fields = "__all__" 10 11 12class TestView(ModelViewSet): 13 queryset = models.Role.objects.all() 14 pagination_class = PageNumberPagination 15 serializer_class = PagerSerializer



10.4 视图的使用
增删改查:ModelViewSet
增删:CreateModelMixin 和 CreateModelMixin 还有 GenericViewSet
复杂逻辑: GenericViewSet 和 APIView
11. DRF路由
11.1 基本路由
urls.py:
1urlpatterns = [ 2 # http://127.0.0.1:8000/api/v1/testview/?format=json 3 re_path('(?P<version>[v1,v2]+)/testview/', views.TestView.as_view({ 4 5 6 'get': 'list','post':'create'})), 7 # http://127.0.0.1:8000/api/v1/testview.json 8 re_path('(?P<version>[v1,v2]+)/testview\.(?P<format>\w+)', views.TestView.as_view({ 9 10 11 'get': 'list','post':'create'})), 12 # http://127.0.0.1:8000/api/v1/testview/1?format=json 13 re_path('(?P<version>[v1,v2]+)/testview/(?P<pk>\d+)', views.TestView.as_view({ 14 15 16 'get':'retrieve','delete':'destroy','put':'update','patch':'partial_update'})), 17 # http://127.0.0.1:8000/api/v1/testview/1.json 18 re_path('(?P<version>[v1,v2]+)/testview/(?P<pk>\d+)\.(?P<format>\w+)', views.TestView.as_view({ 19 20 21 'get':'retrieve','delete':'destroy','put':'update','patch':'partial_update'})), 22]




11.2 自动生成路由
Django REST framework渲染器可以自动帮助我们生成路由,查看生成的路由信息:
urls.py:
1from django.urls import re_path, include 2from . import views 3from rest_framework import routers 4 5router = routers.DefaultRouter() 6router.register('x', views.TestView) 7router.register('xx', views.TestView) 8urlpatterns = [ 9 re_path('(?P<version>[v1,v2]+)/', include(router.urls)) 10]
views.py:
1from rest_framework import serializers 2from rest_framework.viewsets import ModelViewSet 3from rest_framework.pagination import PageNumberPagination 4 5 6class PagerSerializer(serializers.ModelSerializer): 7 class Meta: 8 model = models.Role 9 fields = "__all__" 10 11 12class TestView(ModelViewSet): 13 queryset = models.Role.objects.all() 14 pagination_class = PageNumberPagination 15 serializer_class = PagerSerializer

每一个生成四个,分别是增删改查!如果增删改查都使用,则可以使用自动生成!

12. DRF渲染器
12.1 JSONRenderer
直接显示JSON字符串:
urls.py:
1from django.urls import re_path, include 2from . import views 3from rest_framework import routers 4 5router = routers.DefaultRouter() 6router.register('x', views.TestView) 7router.register('xx', views.TestView) 8urlpatterns = [ 9 re_path('(?P<version>[v1,v2]+)/', include(router.urls)) 10]
settings.py:
1REST_FRAMEWORK = { 2 3 4 5 'DEFAULT_RENDERER_CLASSES': [ 6 'rest_framework.renderers.JSONRenderer', 7 'rest_framework.renderers.BrowsableAPIRenderer', 8 ] 9}
views.py:
1from rest_framework import serializers 2from rest_framework.viewsets import ModelViewSet 3from rest_framework.pagination import PageNumberPagination 4from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer 5 6class PagerSerializer(serializers.ModelSerializer): 7 class Meta: 8 model = models.Role 9 fields = "__all__" 10 11 12class TestView(ModelViewSet): 13 renderer_classes = [JSONRenderer] 14 queryset = models.Role.objects.all() 15 pagination_class = PageNumberPagination 16 serializer_class = PagerSerializer

12.2 BrowsableAPIRenderer
显示更好看的压面:
views.py:
1from rest_framework import serializers 2from rest_framework.viewsets import ModelViewSet 3from rest_framework.pagination import PageNumberPagination 4from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer 5 6class PagerSerializer(serializers.ModelSerializer): 7 class Meta: 8 model = models.Role 9 fields = "__all__" 10 11 12class TestView(ModelViewSet): 13 renderer_classes = [JSONRenderer] 14 queryset = models.Role.objects.all() 15 pagination_class = PageNumberPagination 16 serializer_class = PagerSerializer

12.3 AdminRenderer
views.py:
1from rest_framework import serializers 2from rest_framework.viewsets import ModelViewSet 3from rest_framework.pagination import PageNumberPagination 4from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer,AdminRenderer 5 6class PagerSerializer(serializers.ModelSerializer): 7 class Meta: 8 model = models.Role 9 fields = "__all__" 10 11 12class TestView(ModelViewSet): 13 renderer_classes = [JSONRenderer,BrowsableAPIRenderer,AdminRenderer] 14 queryset = models.Role.objects.all() 15 pagination_class = PageNumberPagination 16 serializer_class = PagerSerializer

AdminRenderer一般不用!
本文同步分享在 博客“Erics-1996”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。