• 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:WindowsSystem32driversetc
                # 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
                            
     
     
  • 相关阅读:
    gorilla/mux 的学习
    SwitchyOmega 配置
    golang []byte 和 string相互转换
    golang 并发demo 写入 redis
    VS2010 显示TFS删除项
    WPF之转换器
    DataTemplate的用法
    DynamicResource与StaticResource的区别
    除非Windows Activation Service (WAS)和万维网发布服务(W3SVC)均处于运行状态,否则无法启动网站。
    WPF 可视化树的用途
  • 原文地址:https://www.cnblogs.com/xiaohema/p/8456887.html
Copyright © 2020-2023  润新知