• django的rest framework框架——版本、解析器、序列化


    一、rest framework的版本使用

    1、版本可以写在URL中,通过GET传参,如 http://127.0.0.1:8082/api/users/?version=v1

    (1)自定义类获取版本信息:

    from django.http import JsonResponse
    from rest_framework.views import APIView
    
    
    class ParamVersion(object):
        """获取版本"""
    
        def determine_version(self, request, *args, **kwargs):
            version = request.query_params.get("version")  # 获取请求里面的版本信息
            return version
    
    
    class UsersView(APIView):
        """用户中心"""
        versioning_class = ParamVersion  # 获取版本信息
    
        def get(self, request, *args, **kwargs):
            res = {"code": 1000, "msg": None, "data": None}
            version = request.version
            print(version)
            return JsonResponse(res)
    views.py

    (2)通过内置类来获取版本

    from django.http import JsonResponse
    from rest_framework.views import APIView
    from rest_framework.versioning import QueryParameterVersioning
    
    
    class UsersView(APIView):
        """用户中心"""
        versioning_class = QueryParameterVersioning  # 获取版本信息
    
        def get(self, request, *args, **kwargs):
            res = {"code": 1000, "msg": None, "data": None}
            version = request.version
            print(version)
            return JsonResponse(res)
    views.py

    在项目的settings中进行参数配置:

    2、也可以写在URL的路径中,如 http://127.0.0.1:8082/api/v1/users/

    可以通过内置类来获取:

    from django.http import JsonResponse
    from rest_framework.views import APIView
    from rest_framework.versioning import URLPathVersioning
    
    class UsersView(APIView):
        """用户中心"""
        versioning_class = URLPathVersioning  # 获取版本信息
    
        def get(self, request, *args, **kwargs):
            res = {"code": 1000, "msg": None, "data": None}
            version = request.version
            print(version)
            return JsonResponse(res)
    views.py

    推荐使用第二种方法来配置版本信息。

    3、可以将版本类写到配置文件中,进行全局控制:

    在项目的settings中进行设置:

    from django.http import JsonResponse
    from rest_framework.views import APIView
    
    class UsersView(APIView):
        """用户中心"""
    
        def get(self, request, *args, **kwargs):
            res = {"code": 1000, "msg": None, "data": None}
            version = request.version  # 获取版本
            print(version)
            return JsonResponse(res)
    views.py

    4、源码流程

    路由→as_view()→rest framework的dispatch()→initial()→determine_version()

    可以通过request.version获取版本信息

    通过request.versioning_scheme获取处理版本的对象,可以利用这个对象调用reverse()方法来反向生成url:

    from django.conf.urls import url
    from api import views
    
    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/users/$', views.UsersView.as_view(), name="userView"),
    ]
    urls.py
    from django.http import JsonResponse
    from rest_framework.views import APIView
    
    class UsersView(APIView):
        """用户中心"""
    
        def get(self, request, *args, **kwargs):
            res = {"code": 1000, "msg": None, "data": None}
            version = request.version  # 获取版本
            scheme = request.versioning_scheme  # 处理版本的对象
    
            # 反向生成URL
            url = scheme.reverse(viewname="userView", request=request)
            print(url)
    
            return JsonResponse(res)
    views.py

    二、解析器

    解析器:对请求体的数据进行解析

    1、Django的解析器

    from django.core.handlers.wsgi import WSGIRequest

    在Django内部会跟据不同的情况将request.body里面的数据解析到request.POST:

    所以只有当 Content_type='application/x-www-form-urlencoded' 时,才会去解析request.body里面的数据,另外,数据格式必须是 name=amy&age=18 这种格式才能被解析,

    基于如上两个条件才能使得request.POST有值。

    2、rest framework的解析器

    1、示例

    from django.http import JsonResponse
    from rest_framework.views import APIView
    from rest_framework.parsers import JSONParser
    
    
    class ExampleView02(APIView):
        """"""
        parser_classes = [JSONParser]  # 解析器类JSONParser:表示允许用户发送json格式的数据{"name":"amy", "age":18}
    
        def post(self, request, *args, **kwargs):
            res = {"code": 1000, "msg": None, "data": None}
            data = request.data  # 获取解析后的数据
    
            res["data"] = data
            return JsonResponse(res)
    views.py

    JSONParser类只能解析Content-type为applicaton/json的数据,

    FormParser类只能解析Content-type为applicaton/x-www-form-urlencoded的数据,

    3、rest framework的解析器源码流程

    执行dispatch()中的initialize_request()方法:

    • 全局解析器配置:

    • 视图中配置:

    在视图中调用requst.data触发:

    request的data方法调用_load_data_and_files():

    以JSONParser类为例:

     三、rest framework序列化处理数据

    1、示例1  序列化类的使用

    from django.db import models
    
    
    class UserGroup(models.Model):
        title = models.CharField(max_length=32)
    
    
    class UserInfo(models.Model):
        user_type_choices = (
            (1, "普通用户"),
            (2, "vip"),
            (3, "svip"),
        )
        user_type = models.IntegerField(choices=user_type_choices)
        username = models.CharField(max_length=32, unique=True)
        password = models.CharField(max_length=64)
        group = models.ForeignKey(to="UserGroup", on_delete=models.CASCADE, null=True, blank=True)
        roles = models.ManyToManyField(to="Role", blank=True)
    
    
    class UserToken(models.Model):
        user = models.OneToOneField(to="UserInfo")
        token = models.CharField(max_length=64)
    
    
    class Role(models.Model):
        title = models.CharField(max_length=32)
    
    
    class Order(models.Model):
        id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=32)
        price = models.DecimalField(max_digits=5, decimal_places=2)
        create_time = models.DateTimeField(auto_now_add=True)
        user = models.ForeignKey(to="UserInfo", on_delete=models.CASCADE, null=True, blank=True)
    models.py
    from django.conf.urls import url
    from api import views
    
    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/roles/$', views.RolesView.as_view()),
    ]
    api/urls.py
    import json
    
    from django.shortcuts import HttpResponse
    from rest_framework.views import APIView
    from rest_framework import serializers
    
    from api import models
    
    
    class RolesSerializer(serializers.Serializer):
        """对获取的数据进行序列化操作"""
        # 要与数据表中的字段向对应
        id = serializers.CharField()
        title = serializers.CharField()
    
    
    class RolesView(APIView):
        """获取角色"""
    
        def get(self, request, *args, **kwargs):
    
            # 方法一:
            # roles = models.Role.objects.all().values("id", "title")
            # roles = list(roles)
            # res = json.dumps(roles, ensure_ascii=False)
    
            # 方法二:使用序列化类
            # roles = models.Role.objects.all()
            # # 实例化序列化类  如果是对多条数据进行序列化要设置 many=True
            # serl = RolesSerializer(instance=roles, many=True)
            # res = serl.data  # 获取序列化后的结果
            # res = json.dumps(res, ensure_ascii=False)
    
            role = models.Role.objects.all().first()
            # 实例化序列化类  如果是对一条数据进行序列化要设置 many=False
            serl = RolesSerializer(instance=role, many=False)
            res = serl.data  # 获取序列化后的结果
            res = json.dumps(res, ensure_ascii=False)
    
            return HttpResponse(res)
    views.py

    2、序列化自定义字段

    from django.conf.urls import url
    from api import views
    
    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/userInfo/$', views.UserInfoView.as_view()),
    ]
    urls.py
    import json
    
    from django.shortcuts import HttpResponse
    from rest_framework.views import APIView
    from rest_framework import serializers
    
    from api import models
    
    
    class UserInfoSerializer(serializers.Serializer):
        """对获取的数据进行序列化操作"""
        username = serializers.CharField()
        password = serializers.CharField()
        user_type = serializers.CharField(source="get_user_type_display")  # 获取choices字段的文本
        group = serializers.CharField(source="group.title")  # 获取ForeignKey字段
        roles = serializers.SerializerMethodField()  # 自定义显示ManyToMany字段
    
        def get_roles(self, row):
            """获取角色对象信息"""
            role_obj_list = row.roles.all()
            res = []
            for item in role_obj_list:
                res.append({"id": item.id, "title": item.title})
            return res
    
    
    class UserInfoView(APIView):
        """获取用户信息"""
    
        def get(self, request, *args, **kwargs):
            users = models.UserInfo.objects.all()
            serl = UserInfoSerializer(instance=users, many=True)
            res = json.dumps(serl.data, ensure_ascii=False)
            return HttpResponse(res)
    views.py

    3、除了继承Serializer类实现序列化,还可以使用ModelSerializer类来实现。ModelSerializer继承了Serializer

    import json
    
    from django.shortcuts import HttpResponse
    from rest_framework.views import APIView
    from rest_framework import serializers
    
    from api import models
    
    
    class UserInfoSerializer(serializers.ModelSerializer):
        """对获取的数据进行序列化操作"""
        class Meta:
            model = models.UserInfo
            fields = "__all__"  # 对所有的字段做序列化
    
    
    class UserInfoView(APIView):
        """获取用户信息"""
    
        def get(self, request, *args, **kwargs):
            users = models.UserInfo.objects.all()
            serl = UserInfoSerializer(instance=users, many=True)
            res = json.dumps(serl.data, ensure_ascii=False)
            return HttpResponse(res)
    views.py

    这种方式生成的数据比较”简陋“,可以根据需求定制:

    class UserInfoSerializer(serializers.ModelSerializer):
        """对获取的数据进行序列化操作"""
        user_type = serializers.CharField(source="get_user_type_display")
        group = serializers.CharField(source="group.title")
        roles = serializers.SerializerMethodField()  # 自定义显示ManyToMany字段
    
        def get_roles(self, row):
            """获取角色对象信息"""
            role_obj_list = row.roles.all()
            res = []
            for item in role_obj_list:
                res.append({"id": item.id, "title": item.title})
            return res
    
        class Meta:
            model = models.UserInfo
            fields = ["id", "username", "password", "user_type", "group", "roles"]  
    
    
    class UserInfoView(APIView):
        """获取用户信息"""
    
        def get(self, request, *args, **kwargs):
            users = models.UserInfo.objects.all()
            serl = UserInfoSerializer(instance=users, many=True)
            res = json.dumps(serl.data, ensure_ascii=False)
            return HttpResponse(res)
    views.py

    4、序列化深度控制

    自动序列化实现连表操作

    class UserInfoSerializer(serializers.ModelSerializer):
        """对获取的数据进行序列化操作"""
    
        class Meta:
            model = models.UserInfo
            fields = "__all__"
            depth = 1  # 深度控制  建议取0~10层,层数越多响应速度也会受到影响
    
    
    class UserInfoView(APIView):
        """获取用户信息"""
    
        def get(self, request, *args, **kwargs):
            users = models.UserInfo.objects.all()
            serl = UserInfoSerializer(instance=users, many=True)
            res = json.dumps(serl.data, ensure_ascii=False)
            return HttpResponse(res)
    views.py

    5、序列化生成HyperMediaLink

    需求:通过查看用户信息页面获取到查看分组信息的URL

    from django.conf.urls import url
    from api import views
    
    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/userInfo/$', views.UserInfoView.as_view()),
        url(r'^(?P<version>[v1|v2]+)/group/(?P<pk>d+)$', views.GroupView.as_view(), name="groupView"),
    ]
    urls.py
    import json
    
    from django.shortcuts import HttpResponse
    from rest_framework.views import APIView
    from rest_framework import serializers
    
    from api import models
    
    
    class GroupSerializer(serializers.ModelSerializer):
        """对获取的数据进行序列化操作"""
    
        class Meta:
            model = models.UserGroup
            fields = "__all__"
    
    
    class UserInfoSerializer(serializers.ModelSerializer):
        """对获取的数据进行序列化操作"""
        # 反向生成查看group的URL
        group = serializers.HyperlinkedIdentityField(view_name="groupView", lookup_field="group_id", lookup_url_kwarg="pk")
    
        class Meta:
            model = models.UserInfo
            fields = ["id", "username", "password", "user_type", "group"]
    
    
    class GroupView(APIView):
        """获取分组信息"""
    
        def get(self, request, *args, **kwargs):
            pk = kwargs.get("pk")
            group_obj = models.UserGroup.objects.filter(id=pk).first()
            serl = GroupSerializer(instance=group_obj, many=False)
            res = json.dumps(serl.data, ensure_ascii=False)
            return HttpResponse(res)
    
    
    class UserInfoView(APIView):
        """获取用户信息"""
    
        def get(self, request, *args, **kwargs):
            users = models.UserInfo.objects.all()
            serl = UserInfoSerializer(instance=users, many=True, context={"request": request})
            res = json.dumps(serl.data, ensure_ascii=False)
            return HttpResponse(res)
    views.py

    6、序列化源码流程

    如果是处理QuerySet数据,就会执行many_init()方法:

    相当于在内部,如果是处理对象(一条数据),就使用Serializer进行处理;如果是处理QuerySet(多条数据),就用ListSerializer处理。

    实例化完成后,可以通过“对象名.data”方法 获取数据:

    父类的data方法:

    如果是处理对象(一条数据),就到Serializer类里面找to_representation()方法;如果是处理QuerySet(多条数据),就去ListSerializer里面找。

     

    get_attribute()方法的具体实现:

    循环字段列表获取字段对应的值:

      获取instance对象对应的字段的属性值,然后把这个属性值赋值给instance,然后继续循环,直到instance从字段里面获取不到值为止,即取数据表中真正的数值,

    如 UserInfo 表中有外键group,想要获取到group字段对应的表对象的title字段:group = serializers.CharField(source="group.title"),则此时attrs=['group', 'title'],

    第一轮循环时:instance=getattr(UserInfo object,group)=UserGroup object,

    第二轮循环时instance=getattr(UserGroup object,title)=A组,此时便通过外键获取到UserGroup表中的title字段的值了。

      当字段是一个可以被调用的对象(如函数或方法)时,就在这个字段后面加上“()”,直接执行这个函数或者方法,将返回值赋值给instance。

    如 获取一个choices字段的文本:user_type=serializers.CharField(source="get_user_type_display") ,这个get_user_type_display就是一个可调用的方法。

    对于字段是serializers.HyperlinkedIdentityField()对象的时候执行的to_representation()方法:

    所以,我们在使用HyperlinkedIdentityField()的时候要传递参数:

    四、rest framework序列化验证用户请求数据

    1、自定义类 验证用户请求数据

    from django.conf.urls import url
    from api import views
    
    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/userGroup/$', views.UserGroupView.as_view()),
    ]
    urls.py
    import json
    
    from django.shortcuts import HttpResponse
    from rest_framework.views import APIView
    from rest_framework import serializers
    
    from api import models
    
    
    class MyValidator(object):
        """自定制校验规则"""
        def __init__(self, base):
            self.base = base
    
        def __call__(self, value):
            # 设置一个必须以self.base开头的规则
            if not value.startswith(self.base):
                message = "this field must start with %s." % self.base
                raise serializers.ValidationError(message)
    
        def set_context(self, serializer_field):
            pass
    
    
    class UserGroupSerializer(serializers.Serializer):
        title = serializers.CharField(
            error_messages={"required": "标题不能为空"},
            validators=[MyValidator("a-")],
        )  # 校验的字段:title
    
    
    class UserGroupView(APIView):
        """验证用户请求数据"""
    
        def post(self, request, *args, **kwargs):
            # request.data :用户提交过来的数据
            serl = UserGroupSerializer(data=request.data)
    
            # 如果请求数据是合法的,就获取到的数据,否则打印错误信息
            if serl.is_valid():
                print(serl.validated_data["title"])
            else:
                print(serl.errors)
            return HttpResponse("xxx")
    views.py

    2、源码

    在Serializer类中实现了请求数据校验:

    from django.conf.urls import url
    from api import views
    
    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/userGroup/$', views.UserGroupView.as_view()),
    ]
    urls.py
    from django.shortcuts import HttpResponse
    from rest_framework.views import APIView
    from rest_framework import serializers
    from api import models
    
    
    class UserGroupSerializer(serializers.Serializer):
        title = serializers.CharField(
            error_messages={"required": "标题不能为空"},
            # validators=[MyValidator("a-")],
        )  # 校验的字段:title
    
        def validate_title(self, value):
            """自定义title字段钩子方法"""
            if not value.startswith("a-"):
                message = "值必须以'a-'开头."
                raise serializers.ValidationError(message)
            return value
    
    
    class UserGroupView(APIView):
        """验证用户请求数据"""
    
        def post(self, request, *args, **kwargs):
            # request.data :用户提交过来的数据
            serl = UserGroupSerializer(data=request.data)
    
            # 如果请求数据是合法的,就获取到的数据,否则打印错误信息
            if serl.is_valid():
                print(serl.validated_data["title"])
            else:
                print(serl.errors)
            return HttpResponse("xxx")
    views.py
  • 相关阅读:
    STM32—LAN8720学习
    STM32F4以太网MAC接口
    分治思想应用题
    网易笔试编程题
    python正则表达式
    【论文笔记】CenterNet: Keypoint Triplets for Object Detection
    【论文笔记】Guided Anchor:Region Proposal by Guided Anchoring
    【论文笔记】SNIP:An Analysis of Scale Invariance in Object Detection
    【论文笔记】FCOS:Fully Convolutional One-Stage Object Detection
    【论文笔记】CenterNet:Objects as Points
  • 原文地址:https://www.cnblogs.com/yanlin-10/p/10252550.html
Copyright © 2020-2023  润新知