python之路_rest-framework之版本、解析器、序列化
一、版本控制
1、基于url传参
如通过这样的url传参方式实现版本控制:http://127.0.0.1:8080/api/users/?version=v2,其中参数名称version和允许的版本通过如下配置文件方式实现,不再允许的版本内的版本是无法通过访问的。
配置文件内容:
REST_FRAMEWORK = { 'DEFAULT_VERSION': 'v1', #默认的版本 'ALLOWED_VERSIONS': ['v1','v2'], #允许的版本 'VERSION_PARAM': 'version', #url中获取值的可以 }
具体使用实例:
url代码:
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r'^test/', TestView.as_view(),name='test'), ]
视图代码:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.versioning import QueryParameterVersioning class TestView(APIView): versioning_class = QueryParameterVersioning def get(self, request, *args, **kwargs): print(request.version) # 获取版本 print(request.versioning_scheme) # 获取版本管理的类 if request.version == "v1": data="这是版本一" elif request.version == "v2": data= "这是版本二" else: data="版本错误" reverse_url = request.versioning_scheme.reverse('test', request=request) # 反向生成URL print(reverse_url) return Response(data)
2、基于url正则
如通过这样的url正则方式实现版本控制:http://127.0.0.1:8080/api/v1/users,其中配置方式与上述一致。具体实例如下:
配置文件:
REST_FRAMEWORK = { 'DEFAULT_VERSION': 'v1', # 默认版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本 'VERSION_PARAM': 'version' # URL中获取值的key }
url代码:
from django.conf.urls import url, include from app01.views import TestView urlpatterns = [ url(r'^(?P<version>[v1|v2]+)/test/', TestView.as_view(), name='test'),
视图代码:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.versioning import URLPathVersioning class TestView(APIView): versioning_class = URLPathVersioning def get(self, request, *args, **kwargs): print(request.version) #获取版本 print(request.versioning_scheme) #获取版本管理的类 if request.version == "v1": data="这是版本一" elif request.version == "v2": data= "这是版本二" else: data="版本错误" reverse_url = request.versioning_scheme.reverse('test', request=request) #反向生成URL print(reverse_url) return Response(data)
3、基于主机名
如:v1.example.com,主要实现实例如下,注意代码在进行本地测试的时候需要在电脑的host文件中配置主机名对应的主机名,如127.0.0.1 v1.example.com
配置代码:
ALLOWED_HOSTS = ['*'] #允许所有的主机名 REST_FRAMEWORK = { 'DEFAULT_VERSION': 'v1', # 默认版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本 'VERSION_PARAM': 'version' # URL中获取值的key }
url代码:
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r'^test/', TestView.as_view(), name='test'), ]
视图代码:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.versioning import HostNameVersioning class TestView(APIView): versioning_class = HostNameVersioning def get(self, request, *args, **kwargs): print(request.version) #获取版本 print(request.versioning_scheme) #获取版本管理的类 if request.version == "v1": data="这是版本一" elif request.version == "v2": data= "这是版本二" else: data="版本错误" reverse_url = request.versioning_scheme.reverse('test', request=request) #反向生成URL print(reverse_url) return Response(data)
4、全局使用
以上实例中均是在局部视图使用的情况,如果想在全局使用,只需要按照如下方式进行配置,局部视图不用再做任何的配置即可使用。具体的配置方式如下:
REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning", #使用哪一种,配置对应的版本类 'DEFAULT_VERSION': 'v1', 'ALLOWED_VERSIONS': ['v1', 'v2'], 'VERSION_PARAM': 'version' }
二、解析器
对请求体中的数据进行解析的作用。只有在执行request.data时才出发解析器。具体应用实例如下:
1、仅处理请求头content-type为application/json的请求体
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.request import Request from rest_framework.parsers import JSONParser class TestView(APIView): parser_classes = [JSONParser, ] def post(self, request, *args, **kwargs): 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请求,响应内容')
2、仅处理请求头content-type为application/x-www-form-urlencoded 的请求体
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.request import Request from rest_framework.parsers import FormParser class TestView(APIView): parser_classes = [FormParser, ] def post(self, request, *args, **kwargs): 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请求,响应内容')
3、全局使用
只需要在配置文件中做如下配置即可:
REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES':[ 'rest_framework.parsers.JSONParser' 'rest_framework.parsers.FormParser' 'rest_framework.parsers.MultiPartParser' ] }
总结:如果客户端的Content-Type的值和 application/json 匹配:JSONParser处理数据;如果客户端的Content-Type的值和 application/x-www-form-urlencoded 匹配:FormParser处理数据。
注意:个别特殊的值可以通过Django的request对象 request._request 来进行获取,其他解析器请参看博客:http://www.cnblogs.com/wupeiqi/articles/7805382.html
三、序列化
对于序列化的概念,我们并不陌生。在restful中序列化是将查询的queryset对象序列化成有序字典的过程。具体方式介绍如下,实例中均以如下表结构为依据:
from django.db import models class Menu(models.Model): name = models.CharField(max_length=32) 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 Role(models.Model): name = models.CharField(max_length=32)
1、基于Serializer
a.基本操作
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers from . import models class UsersSerializer(serializers.Serializer): name = serializers.CharField() pwd = serializers.CharField() class UsersView(APIView): def get(self, request, *args, **kwargs): # 方式一(未序列化): user_list = models.UserInfo.objects.all().values('name','pwd','group__id',"group__title") return Response(user_list) # 方式二之多对象 user_list = models.UserInfo.objects.all() ser = UsersSerializer(instance=user_list,many=True) #many=True return Response(ser.data) # 方式二之单对象 user = models.UserInfo.objects.all().first() ser = UsersSerializer(instance=user, many=False) #many=False return Response(ser.data)
b.简单跨表
class UsersSerializer(serializers.Serializer): name = serializers.CharField() pwd = serializers.CharField() ''' 跨表字段必须通过source给与指定,如下 group_name和menu_name可以随便定义。 ''' group_name = serializers.CharField(source="group.title") menu_name = serializers.CharField(source="group.mu.name") class UsersView(APIView): def get(self,request,*args,**kwargs): user_list = models.UserInfo.objects.all() ser = UsersSerializer(instance=user_list,many=True) return Response(ser.data)
c.复杂跨表
对于多对多表关系,想要查询并序列化可就没有多对一表关系查询name简单了。需要另外重构一个类处理多对多字段,具体实例如下:
1、方式一
#方式一: class MyCharField(serializers.CharField): ''' 用于查询多对多字段,to_representation方法用于处理查询数据, 本例为例,value接收source="roles.all"参数,可以自定义要查 询的多对多表中的数据及要显示的数据结构。 ''' 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() pwd = serializers.CharField() group_name = serializers.CharField(source="group.title") menu_name = serializers.CharField(source="group.mu.name") x2 = MyCharField(source="roles.all")
2、方式二
#方式二: class MyCharField(serializers.CharField): def to_representation(self, value): return {'id':value.pk, 'name':value.name} class UsersSerializer(serializers.Serializer): name = serializers.CharField() pwd = serializers.CharField() group_id = serializers.CharField() group_name = serializers.CharField(source="group.title") menu_name = serializers.CharField(source="group.mu.name") x2 = serializers.ListField(child=MyCharField(),source="roles.all")
3、方式三(推荐)
class UsersSerializer(serializers.Serializer): name = serializers.CharField() pwd = serializers.CharField() group_name = serializers.CharField(source="group.title") menu_name = serializers.CharField(source="group.mu.name") x2 = serializers.SerializerMethodField() def get_x2(self,obj): #get_x2中的x2必须与上述x2变量名保持一致 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
以上三种实现方式对应的视图相同,如下:
class UsersView(APIView): def get(self,request,*args,**kwargs): user_list = models.UserInfo.objects.all() ser = UsersSerializer(instance=user_list,many=True) return Response(ser.data)
2、基于ModelSerializer
a、基本使用
class UsersSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo fields = "__all__" # fields = ['name', 'pwd','group'] ''' 可以设置0-10间数字,作用:可以让group字段跨表显示相应的组信息, 若设置2,则group表中关联的mu字段对应的表信息也会显示。 ''' depth = 1 class UsersView(APIView): def get(self,request,*args,**kwargs): user_list = models.UserInfo.objects.all() ser = UsersSerializer(instance=user_list,many=True) return Response(ser.data)
b.简单跨表
class UsersSerializer(serializers.ModelSerializer): x1 = serializers.CharField(source='group.title') class Meta: model = models.UserInfo fields = ['name', 'pwd','x1'] #把上述的x1添加到fields中 depth = 1 class UsersView(APIView): def get(self,request,*args,**kwargs): user_list = models.UserInfo.objects.all() ser = UsersSerializer(instance=user_list,many=True) return Response(ser.data)
c.反向生成url
class UsersSerializer(serializers.ModelSerializer): group = serializers.HyperlinkedIdentityField(view_name='detail') #view_name为url别名 class Meta: model = models.UserInfo fields = ['name', 'pwd','group'] #返回的group反向解析的url,group为随便起的变量 depth = 1 class UsersView(APIView): def get(self,request,*args,**kwargs): user_list = models.UserInfo.objects.all() ser = UsersSerializer(instance=user_list,many=True,context={'request':request}) #必须有,context={'request':request} return Response(ser.data)
原url为:url(r'^xxx/(?P<pk>\d+)', views.UsersView.as_view(),name='detail'),参数必须为pk,反向生成的url中pk对应的条信息的id。
3、基于HyperlinkedModelSerializer
全局生成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): user_list = models.UserInfo.objects.all() ser = UsersSerializer(instance=user_list,many=True,context={'request':request}) return Response(ser.data)
其中原url为:
''' 以下url别名必须为表名或者关联字段加detail组成,最终userinfo-detail别名会生成包含该条信息id的url,group-detail会生成包含对应组id的url。。。 ''' url(r'^xxx/(?P<pk>\d+)', views.UsersView.as_view(), name='userinfo-detail'), url(r'^xxx/(?P<pk>\d+)', views.UsersView.as_view(),name='group-detail'), url(r'^xxx/(?P<pk>\d+)', views.UsersView.as_view(),name='role-detail'),
四、请求数据验证
1、场景一
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')]) #用于对post数据的校验 class UsersView(APIView): 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('...')
2、场景二
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.ModelSerializer): class Meta: model = models.UserInfo fields = "__all__" extra_kwargs = { 'name': {'min_length': 6}, 'pwd': {'validators': [PasswordValidator(666), ]} } #用于对post数据的校验 class UsersView(APIView): 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('...')