Django Restful Framework【第四篇】版本、解析器、序列化和请求数据验证
一、版本
程序也来越大时,可能通过版本不同做不同的处理
没用rest_framework之前,我们可以通过以下这样的方式去获取。
class UserView(APIView): def get(self,request,*args,**kwargs): version = request.query_params.get('version') print(version) if version=='v1': #如果版本是v1 ret = { 'code':111, 'msg':'版本一的内容' } elif version=='v2': # 如果是v2 ret = { 'code': 112, 'msg': '版本二的内容' } else: ret = { 'code': 0, 'msg': '不支持其他版本' } return Response(ret)
现在我们来用rest_framework实现一下,有两种方式
1、基于url的方式
#基于url传参的形式 versioning_class = QueryParameterVersioning #http://127.0.0.1:8080/api/users/?version=v2 #基于url的形式 versioning_class = URLPathVersioning #http://127.0.0.1:8080/api/v1/users/
具体步骤
settings配置
REST_FRAMEWORK = { 'DEFAULT_VERSION': 'v1', #默认的版本 'ALLOWED_VERSIONS': ['v1','v2'], #允许的版本 'VERSION_PARAM': 'version', }
urls.py
from django.conf.urls import url,include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^api/(?P<version>[v1|v2]+)/', include('api.urls'), name='users-list'), ]
urls.py
from api import views urlpatterns = [ # url(r'^users/', views.UserView.as_view()), url(r'^users/', views.UserView1.as_view()), ]
views.py
class UserView1(APIView): #基于url传参的形式 # versioning_class = QueryParameterVersioning #http://127.0.0.1:8080/api/users/?version=v2 #基于url的形式 #http://127.0.0.1:8080/api/v1/users/ versioning_class = URLPathVersioning def get(self,request,*args,**kwargs): # self.dispatch print(request.version) #打印的是版本 print(request.versioning_scheme) #打印的是对象 if request.version=='v2': return Response('我是版本二') elif request.version=='v1': return Response('我是版本一') else: return Response('去去去')
注:在配置的时候
REST_FRAMEWORK = { 'VERSION_PARAM':'version', 'DEFAULT_VERSION':'v1', 'ALLOWED_VERSIONS':['v1','v2'], # 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.HostNameVersioning"
#如果加上这个配置就不用versioning_class = QueryParameterVersioning这样在指定了,
'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning" }
附加:restful提供的反向生成
#http://127.0.0.1:8080/api/v1/users/
#urls.py #分发路由 urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^api/(?P<version>[v1|v2]+)/', include('api.urls')), ] #api.urls.py urlpatterns = [ url(r'^users/', views.UserView1.as_view(), name='users-list'), ] #views.py 导入类 from rest_framework.reverse import reverse url = request.versioning_scheme.reverse(viewname='users-list',request=request) print(url)
我们自己用django实现的,当前版本不一样的时候可以用这种方式
from django.urls import reverse url = reverse(viewname='users-list',kwargs={'version':'v2'}) #指定的是v2就是v2,当你路径中输入v1的时候还是v2的路径 print(url) #/api/v2/users/
2、基于子域名传参
#分发url urlpatterns = [ #url(r'^admin/', admin.site.urls), url(r'^api/', include('api.urls')), ] urlpatterns = [ url(r'^users/', views.UsersView.as_view(),name='u'), ] class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch print(request.version) # QueryParameterVersioning().detemiin_version() print(request.versioning_scheme) # QueryParameterVersioning() REST_FRAMEWORK = { 'VERSION_PARAM':'version', 'DEFAULT_VERSION':'v1', 'ALLOWED_VERSIONS':['v1','v2'], 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.HostNameVersioning" } # C:\Windows\System32\drivers\etc # vim /etc/hosts 127.0.0.1 v1.luffy.com 127.0.0.1 v2.luffy.com #配置ALLOWED_HOSTS = ['*']
如果遇到这样的错误
这是由于没有允许,解决办法,在settings里面配置一下
ALLOWED_HOSTS = ['*']
二、解析器:reqest.data取值的时候才执行
对请求的数据进行解析:是针对请求体进行解析的。表示服务器可以解析的数据格式的种类
django中的发送请求
#如果是这样的格式发送的数据,在POST里面有值 Content-Type: application/url-encoding..... request.body request.POST #如果是发送的json的格式,在POST里面是没有值的,在body里面有值,可通过decode,然后loads取值 Content-Type: application/json..... request.body request.POST
为了这种情况下每次都要decode,loads,显得麻烦,所以才有的解析器。弥补了django的缺点
#具体讲解 客户端: Content-Type: application/json '{"name":"alex","age":123}' 服务端接收: 读取客户端发送的Content-Type的值 application/json parser_classes = [JSONParser,FormParser] #表示服务器可以解析的数据格式的种类 media_type_list = ['application/json','application/x-www-form-urlencoded'] 如果客户端的Content-Type的值和 application/json 匹配:JSONParser处理数据 如果客户端的Content-Type的值和 application/x-www-form-urlencoded 匹配:FormParser处理数据 配置: 单视图: class UsersView(APIView): parser_classes = [JSONParser,] 全局配置: REST_FRAMEWORK = { 'VERSION_PARAM':'version', 'DEFAULT_VERSION':'v1', 'ALLOWED_VERSIONS':['v1','v2'], # 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.HostNameVersioning" 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning", 'DEFAULT_PARSER_CLASSES':[ 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', ] } class UserView(APIView): def get(self,request,*args,**kwargs): return Response('ok') def post(self,request,*args,**kwargs): print(request.data) #以后取值就在这里面去取值 return Response('...')
传上传文件
urls.py
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r'test/(?P<filename>[^/]+)', TestView.as_view(), name='test'), ]
views.py
#!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.request import Request from rest_framework.parsers import FileUploadParser class TestView(APIView): parser_classes = [FileUploadParser, ] def post(self, request, filename, *args, **kwargs): print(filename) print(request.content_type) # 获取请求的值,并使用对应的JSONParser进行处理 print(request.data) # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值 print(request.POST) print(request.FILES) return Response('POST请求,响应内容') def put(self, request, *args, **kwargs): return Response('PUT请求,响应内容')
upload.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="http://127.0.0.1:8000/test/f1.numbers" method="post" enctype="multipart/form-data"> <input type="text" name="user" /> <input type="file" name="img"> <input type="submit" value="提交"> </form> </body> </html>
全局使用
settings.py
REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES':[ 'rest_framework.parsers.JSONParser' 'rest_framework.parsers.FormParser' 'rest_framework.parsers.MultiPartParser' ] }
三、序列化
序列化用于对用户请求数据进行验证和数据进行序列化(为了解决queryset序列化问题)。
那什么是序列化呢?序列化就是把对象转换成字符串,反序列化就是把字符串转换成对象
models.py
from django.db import models # Create your models here. class Group(models.Model): title = models.CharField(max_length=32) mu = models.ForeignKey(to='Menu',default=1) class UserInfo(models.Model): name = models.CharField(max_length=32) pwd = models.CharField(max_length=32) group = models.ForeignKey(to="Group") roles = models.ManyToManyField(to="Role") class Menu(models.Model): name = models.CharField(max_length=21) class Role(models.Model): name = models.CharField(max_length=32)
1、基本操作
views.py
from django.shortcuts import render,HttpResponse from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.versioning import BaseVersioning from rest_framework.versioning import QueryParameterVersioning #获取version的值 from rest_framework.versioning import URLPathVersioning #支持版本 from rest_framework.versioning import HostNameVersioning from rest_framework.parsers import JSONParser #解析器 from rest_framework import serializers from app03 import models class UsersSerializer(serializers.Serializer): name = serializers.CharField() #字段名字 pwd = serializers.CharField() class UserView(APIView): def get(self,request,*args,**kwargs): # 方式一实现 # user_list = models.UserInfo.objects.values('name','pwd','group__mu','group__title') # print(type(user_list)) # return Response(user_list) # 方式二之多对象 # user_list = models.UserInfo.objects.all() #直接这样查会报错,借助他提供的系列化 # ser = UsersSerializer(instance=user_list,many=True) #可允许多个 # # print(type(ser)) #<class 'rest_framework.serializers.ListSerializer'> # print(ser.data) #返回的是一个有序字典 #方式三之单对象 user = models.UserInfo.objects.all().first() ser = UsersSerializer(instance=user,many=False) return Response(ser.data)
2、跨表
x1 = serializers.CharField(source='group.mu.name')
如果你想跨表拿你任何需要的数据,都可以用上面的这种操作,内部做判断,如果可用内部就加括号调用了
Views.py
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers from app03 import models class UsersSerializer(serializers.Serializer): name = serializers.CharField() #字段名字 pwd = serializers.CharField() # group = serializers.CharField() #会显示对象 # group_id = serializers.CharField() #会显示id x1 = serializers.CharField(source='group.mu.name') roles = serializers.CharField(source='roles.all') #多对多关系的这样查出的是queryset对象 class UserView2(APIView): '''跨表操作''' def get(self,request,*args,**kwargs): user = models.UserInfo.objects.all() ser = UsersSerializer(instance=user,many=True) return Response(ser.data)
3、复杂序列化
解决方案一:
class MyCharField(serializers.CharField): def to_representation(self, value): ##打印的是所有的数据 data_list = [] for row in value: data_list.append(row.name) return data_list class UsersSerializer(serializers.Serializer): name = serializers.CharField() # obj.name pwd = serializers.CharField() # obj.pwd group_id = serializers.CharField() # obj.group_id xxxx = serializers.CharField(source="group.title") # obj.group.title x1 = serializers.CharField(source="group.mu.name") # obj.mu.name # x2 = serializers.CharField(source="roles.all") # 多对多关系的这样查出的是queryset对象 x2 = MyCharField(source="roles.all") # obj.mu.name
解决方案二:
class MyCharField(serializers.CharField): def to_representation(self, value): return {'id':value.pk, 'name':value.name} class UsersSerializer(serializers.Serializer): name = serializers.CharField() # obj.name pwd = serializers.CharField() # obj.pwd group_id = serializers.CharField() # obj.group_id xxxx = serializers.CharField(source="group.title") # obj.group.title x1 = serializers.CharField(source="group.mu.name") # obj.mu.name # x2 = serializers.CharField(source="roles.all") # obj.mu.name x2 = serializers.ListField(child=MyCharField(),source="roles.all") # obj.mu.name
解决方案三(推荐使用)
class UsersSerializer(serializers.Serializer): name = serializers.CharField() # obj.name pwd = serializers.CharField() # obj.pwd group_id = serializers.CharField() # obj.group_id xxxx = serializers.CharField(source="group.title") # obj.group.title x1 = serializers.CharField(source="group.mu.name") # obj.mu.name # x2 = serializers.CharField(source="roles.all") # obj.mu.name # x2 = serializers.ListField(child=MyCharField(),source="roles.all") # obj.mu.name x2 = serializers.SerializerMethodField() def get_x2(self,obj): #get_字段名 print(obj) ##UserInfo object obj.roles.all() role_list = obj.roles.filter(id__gt=1) data_list = [] for row in role_list: data_list.append({'pk':row.pk,'name':row.name}) return data_list
4、基于Model
class UsersSerializer(serializers.ModelSerializer): x1 = serializers.CharField(source='name') group = serializers.HyperlinkedIdentityField(view_name='detail') class Meta: model = models.UserInfo # fields = "__all__" fields = ['name','pwd','group','x1'] #自定义字段的时候注意要指定source,scource里面的数据必须是数据库有的数据 depth = 1 #表示深度 class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch # 方式一: # user_list = models.UserInfo.objects.all().values('name','pwd','group__id',"group__title") # return Response(user_list) # 方式二之多对象 user_list = models.UserInfo.objects.all() # [obj1,obj2,obj3] ser = UsersSerializer(instance=user_list,many=True) return Response(ser.data) Views.py
5、生成URL
views.py
class UsersSerializer(serializers.ModelSerializer): # group = serializers.HyperlinkedIdentityField(view_name='detail') class Meta: model = models.UserInfo fields = "__all__" fields = ['name', 'pwd','group'] depth = 1 class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch # 方式一: # user_list = models.UserInfo.objects.all().values('name','pwd','group__id',"group__title") # return Response(user_list) # 方式二之多对象 user_list = models.UserInfo.objects.all() # [obj1,obj2,obj3] ser = UsersSerializer(instance=user_list,many=True,context={'request':request}) return Response(ser.data)
from django.conf.urls import url,include from django.contrib import admin from app03 import views urlpatterns = [ url(r'^users4/', views.UserView4.as_view(), name='xxx'), #吧users4的group的值反向生成users5的url url(r'^users5/(?P<pk>.*)', views.UserView5.as_view(), name='detail'), #必须叫pk # url(r'^users4/(?P<pk>.*)', views.UserView4.as_view(), name='detail'), ]
6、全局生成URL
class UsersSerializer(serializers.HyperlinkedModelSerializer): #继承他自动生成 class Meta: model = models.UserInfo fields = "__all__" # fields = ['id','name','pwd'] class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch # 方式一: # user_list = models.UserInfo.objects.all().values('name','pwd','group__id',"group__title") # return Response(user_list) # 方式二之多对象 user_list = models.UserInfo.objects.all() # [obj1,obj2,obj3] ser = UsersSerializer(instance=user_list,many=True,context={'request':request}) return Response(ser.data)
四、请求数据验证:
a、自己手写
class PasswordValidator(object): def __init__(self, base): self.base = base def __call__(self, value): if value != self.base: message = '用户输入的值必须是 %s.' % self.base raise serializers.ValidationError(message) def set_context(self, serializer_field): """ This hook is called by the serializer instance, prior to the validation call being made. """ # 执行验证之前调用,serializer_fields是当前字段对象 pass class UsersSerializer(serializers.Serializer): name = serializers.CharField(min_length=6) pwd = serializers.CharField(error_messages={'required': '密码不能为空'}, validators=[PasswordValidator('666')])
b、基于model
class PasswordValidator(object): def __init__(self, base): self.base = base def __call__(self, value): if value != self.base: message = '用户输入的值必须是 %s.' % self.base raise serializers.ValidationError(message) def set_context(self, serializer_field): """ This hook is called by the serializer instance, prior to the validation call being made. """ # 执行验证之前调用,serializer_fields是当前字段对象 pass class UsersSerializer(serializers.Serializer): name = serializers.CharField(min_length=6) pwd = serializers.CharField(error_messages={'required': '密码不能为空'}, validators=[PasswordValidator('666')])
使用
class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch # 方式一: # user_list = models.UserInfo.objects.all().values('name','pwd','group__id',"group__title") # return Response(user_list) # 方式二之多对象 user_list = models.UserInfo.objects.all() # [obj1,obj2,obj3] ser = UsersSerializer(instance=user_list,many=True,context={'request':request}) return Response(ser.data) def post(self,request,*args,**kwargs): ser = UsersSerializer(data=request.data) if ser.is_valid(): print(ser.validated_data) else: print(ser.errors) return Response('...')
钩子函数
def validate_字段(self,validated_value): raise ValidationError(detail='xxxxxx') return validated_value