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('...')

 

posted @ 2018-02-25 18:29  骑猪走秀  阅读(377)  评论(0编辑  收藏  举报