• 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('...')
  • 相关阅读:
    Python变量状态保持四种方法
    Python参数基础
    Django Form 表单
    Python开发第四篇
    Python开发第三篇
    设计模式(一)概述
    Python自学之路——自定义简单装饰器
    Python开发第二篇
    Python开发第一篇
    Python核心编程——多线程threading和队列
  • 原文地址:https://www.cnblogs.com/seven-007/p/8470103.html
Copyright © 2020-2023  润新知