Django rest framework源码分析----序列化 - Go语言中文社区

Django rest framework源码分析----序列化


**第一部分:简单基本使用实例,第二部分:源码的简单分析
由于序列化继承关系太过复杂,很难清晰表达,所以本文的源码只作为自己对知识的记录**

一.序列化的简单使用

1.继承Serializer

基本使用
(1)models.py

from django.db import models

class UserInfo(models.Model):
    USER_TYPE = (
        (1,'普通用户'),
        (2,'VIP'),
        (3,'SVIP')
    )

    user_type = models.IntegerField(choices=USER_TYPE)
    username = models.CharField(max_length=32,unique=True)
    password = models.CharField(max_length=64)
    group = models.ForeignKey('UserGroup',on_delete=models.CASCADE)
    roles = models.ManyToManyField('Role')


class UserToken(models.Model):
    user = models.OneToOneField('UserInfo',on_delete=models.CASCADE)
    token = models.CharField(max_length=64)


class UserGroup(models.Model):
    title = models.CharField(max_length=32)


class Role(models.Model):
    title = models.CharField(max_length=32)

添加Role
在这里插入图片描述
(2)api/urls.py

urlpatterns = [
    re_path('(?P<version>[v1|v2]+)/roles/', RolesView.as_view()),   #序列化
]

(3)views.py
方式一:

import json

from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from . import models


from rest_framework import serializers

#要先写一个序列化的类
class RolesSerializer(serializers.Serializer):
    #Role表里面的字段id和title序列化
    id = serializers.IntegerField()
    title = serializers.CharField()

class RolesView(APIView):
    def get(self,request,*args,**kwargs):
        # 方式一:对于[obj,obj,obj]
        # (Queryset)
        roles = models.Role.objects.all()
        # 序列化,两个参数,instance:接受Queryset(或者对象)   mangy=True表示对Queryset进行处理,mant=False表示对对象进行进行处理
        ser = RolesSerializer(instance=roles,many=True)
        # 转成json格式,ensure_ascii=False表示显示中文,默认为True
        ret = json.dumps(ser.data,ensure_ascii=False)
        return HttpResponse(ret)

(4)浏览器访问:http://127.0.0.1:8000/api/v1/roles/

可以显示后台返回的json数据
在这里插入图片描述
方式二

class RolesView(APIView):
    def get(self,request,*args,**kwargs):
        # 方式一:对于[obj,obj,obj]
        # (Queryset)
        # roles = models.Role.objects.all()
        # 序列化,两个参数,instance:Queryset  如果有多个值,就需要加 mangy=True
        # ser = RolesSerializer(instance=roles,many=True)
        # 转成json格式,ensure_ascii=False表示显示中文,默认为True
        # ret = json.dumps(ser.data,ensure_ascii=False)

        # 方式二:
        role = models.Role.objects.all().first()
        ser = RolesSerializer(instance=role, many=False)
        ret = json.dumps(ser.data, ensure_ascii=False)
        return HttpResponse(ret)

只获取一个

在这里插入图片描述

**小结:请看view.py文件中的注释内容**

进阶使用

(1)urls.py

添加一个info

urlpatterns = [
    re_path('(?P<version>[v1|v2]+)/roles/', RolesView.as_view()),   #序列化
    re_path('(?P<version>[v1|v2]+)/info/', UserInfoView.as_view()),   #序列化
]

(2)views.py

class UserInfoSerializer(serializers.Serializer):
    '''序列化用户的信息'''
    #user_type是choices(1,2,3),显示全称的方法用source
    type = serializers.CharField(source="get_user_type_display")
    username = serializers.CharField()
    password = serializers.CharField()
    #group.title:组的名字
    group = serializers.CharField(source="group.title")
    #SerializerMethodField(),表示自定义显示
    #然后写一个自定义的方法
    rls = serializers.SerializerMethodField()

    def get_rls(self,row):
        #获取用户所有的角色
        role_obj_list = row.roles.all()
        ret = []
        #获取角色的id和名字
        #以字典的键值对方式显示
        for item in role_obj_list:
            ret.append({"id":item.id,"title":item.title})
        return ret

class UserInfoView(APIView):
    '''用户的信息'''
    def get(self,request,*args,**kwargs):
        users = models.UserInfo.objects.all()
        ser = UserInfoSerializer(instance=users,many=True)
        ret = json.dumps(ser.data,ensure_ascii=False)
        return HttpResponse(ret)

自定义方法
在这里插入图片描述
(3)浏览器访问:http://127.0.0.1:8000/api/v1/info/
在这里插入图片描述

2.继承 ModelSerializer

(1)把上面的UserInfoSerializer改成继承ModelSerializer的用法

# class UserInfoSerializer(serializers.Serializer):
#     '''序列化用户的信息'''
#     #user_type是choices(1,2,3),显示全称的方法用source
#     type = serializers.CharField(source="get_user_type_display")
#     username = serializers.CharField()
#     password = serializers.CharField()
#     #group.title:组的名字
#     group = serializers.CharField(source="group.title")
#     #SerializerMethodField(),表示自定义显示
#     #然后写一个自定义的方法
#     rls = serializers.SerializerMethodField()
#
#     def get_rls(self,row):
#         #获取用户所有的角色
#         role_obj_list = row.roles.all()
#         ret = []
#         #获取角色的id和名字
#         #以字典的键值对方式显示
#         for item in role_obj_list:
#             ret.append({"id":item.id,"title":item.title})
#         return ret


class UserInfoSerializer(serializers.ModelSerializer):
"""
特殊显示的字段,比如type,group,rls等不变,也需要特殊写出来,
不需要特殊序列化的字段,写在Meta中field即可
"""
	type = serializers.CharField(source="get_user_type_display")
    group = serializers.CharField(source="group.title")
    rls = serializers.SerializerMethodField()

    def get_rls(self, row):
        # 获取用户所有的角色
        role_obj_list = row.roles.all()
        ret = []
        # 获取角色的id和名字
        # 以字典的键值对方式显示
        for item in role_obj_list:
            ret.append({"id": item.id, "title": item.title})
        return ret

    class Meta:
        model = models.UserInfo
        fields = ['id','username','password','type','group','rls']

class UserInfoView(APIView):
    '''用户的信息'''
    def get(self,request,*args,**kwargs):
        users = models.UserInfo.objects.all()
        ser = UserInfoSerializer(instance=users,many=True)
        ret = json.dumps(ser.data,ensure_ascii=False)
        return HttpResponse(ret)

结果一模一样
在这里插入图片描述

3.自动序列化连表(depth)

继续优化上面的代码,用depth更简单方便

class UserInfoSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        #fields = "__all__"
        fields = ['id','username','password','group','roles']
        #表示连表的深度
        depth = 1

class UserInfoView(APIView):
    '''用户的信息'''
    def get(self,request,*args,**kwargs):
        users = models.UserInfo.objects.all()
        ser = UserInfoSerializer(instance=users,many=True)
        ret = json.dumps(ser.data,ensure_ascii=False)
        return HttpResponse(ret)

访问:http://127.0.0.1:8000/api/v1/info/
在这里插入图片描述

4.生成url

(1)url.py

urlpatterns = [
    re_path('(?P<version>[v1|v2]+)/group/(?P<pk>d+)/', GroupView.as_view(),name = 'gp')    #序列化生成url
]

(2)views.py

class UserInfoSerializer(serializers.ModelSerializer):
# 通过lookup_field='group_id',lookup_url_kwarg='pk'
# group = serializers.HyperlinkedIdentityField(view_name='api:gp', lookup_field='group_id', lookup_url_kwarg='pk')
#lookup_url_kwarg='pk'中的pk要与url中的参数一致,也可以时别的参数
    group = serializers.HyperlinkedIdentityField(view_name='gp',lookup_field='group_id',lookup_url_kwarg='pk')
    class Meta:
        model = models.UserInfo
        #fields = "__all__"
        fields = ['id','username','password','group','roles']
        #表示连表的深度
        depth = 0


class UserInfoView(APIView):
    '''用户的信息'''
    def get(self,request,*args,**kwargs):
        users = models.UserInfo.objects.all()
        #这里必须要传参数context={'request':request}
        ser = UserInfoSerializer(instance=users,many=True,context={'request':request})
        ret = json.dumps(ser.data,ensure_ascii=False)
        return HttpResponse(ret)


class GroupSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserGroup
        fields = "__all__"

class GroupView(APIView):
    def get(self,request,*args,**kwargs):
        pk = kwargs.get('pk')
        obj = models.UserGroup.objects.filter(pk=pk).first()

        ser = GroupSerializer(instance=obj,many=False)
        ret = json.dumps(ser.data,ensure_ascii=False)
        return HttpResponse(ret)

访问:http://127.0.0.1:8000/api/v1/info/

可以获取到group的url
在这里插入图片描述

5.用户请求数据验证

基本验证

(1)url.py

urlpatterns = [
    re_path('(?P<version>[v1|v2]+)/usergroup/', UserGroupView.as_view(),)    #序列化做验证
]

(2)views.py

class UserGroupSerializer(serializers.Serializer):
    title = serializers.CharField()

class UserGroupView(APIView):
    def post(self,request,*args, **kwargs):
        ser = UserGroupSerializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data['title'])
        else:
            print(ser.errors)

        return HttpResponse("用户提交数据验证")

用postman发送正确的数据,后台可以拿到
在这里插入图片描述
在这里插入图片描述
自定义验证规则

(1)views.py

添加一个自定义验证

#自定义验证规则
class GroupValidation(object):
    def __init__(self,base):
        self.base = base

    def __call__(self, value):
        if not value.startswith(self.base):
            message = "标题必须以%s为开头"%self.base
            raise serializers.ValidationError(message)


class UserGroupSerializer(serializers.Serializer):
    title = serializers.CharField(validators=[GroupValidation('以我开头'),])    

class UserGroupView(APIView):
    def post(self,request,*args, **kwargs):
        ser = UserGroupSerializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data['title'])
        else:
            print(ser.errors)

        return HttpResponse("用户提交数据验证")

提交不合法的数据
在这里插入图片描述
后台报错
在这里插入图片描述
提交正确的数据
在这里插入图片描述
在这里插入图片描述

二.源码分析

实例化源码分析

 ser = UserInfoSerializer(instance=users, many=True)

以上面的示例代码为例
在BaseSerializer类中实例化(仔细去继承的父类中找)

class BaseSerializer(Field):
    """
    The BaseSerializer class provides a minimal class which may be used
    for writing custom serializer implementations.

    Note that we strongly restrict the ordering of operations/properties
    that may be used on the serializer in order to enforce correct usage.

    In particular, if a `data=` argument is passed then:

    .is_valid() - Available.
    .initial_data - Available.
    .validated_data - Only available after calling `is_valid()`
    .errors - Only available after calling `is_valid()`
    .data - Only available after calling `is_valid()`

    If a `data=` argument is not passed then:

    .is_valid() - Not available.
    .initial_data - Not available.
    .validated_data - Not available.
    .errors - Not available.
    .data - Available.
    """

    def __init__(self, instance=None, data=empty, **kwargs):
        self.instance = instance
        if data is not empty:
            self.initial_data = data
        self.partial = kwargs.pop('partial', False)
        self._context = kwargs.pop('context', {})
        kwargs.pop('many', None)
        super().__init__(**kwargs)

    def __new__(cls, *args, **kwargs):
    """如果ser = UserInfoSerializer(instance=users, many=True)在实例化的时候many=True将通过many_init方法返回ListSerializer的实例对象
    如果many=False,将在返回instance对象(前提要知道什么时候为True和false)
    """
    
        # We override this method in order to automagically create
        # `ListSerializer` classes instead when `many=True` is set.
        if kwargs.pop('many', False):
            return cls.many_init(*args, **kwargs)
        return super().__new__(cls, *args, **kwargs)

 	@classmethod
    def many_init(cls, *args, **kwargs):
        """
        This method implements the creation of a `ListSerializer` parent
        class when `many=True` is used. You can customize it if you need to
        control which keyword arguments are passed to the parent, and
        which are passed to the child.

        Note that we're over-cautious in passing most arguments to both parent
        and child classes in order to try to cover the general case. If you're
        overriding this method you'll probably want something much simpler, eg:

        @classmethod
        def many_init(cls, *args, **kwargs):
            kwargs['child'] = cls()
            return CustomListSerializer(*args, **kwargs)
        """
        allow_empty = kwargs.pop('allow_empty', None)
        child_serializer = cls(*args, **kwargs)
        list_kwargs = {
            'child': child_serializer,
        }
        if allow_empty is not None:
            list_kwargs['allow_empty'] = allow_empty
        list_kwargs.update({
            key: value for key, value in kwargs.items()
            if key in LIST_SERIALIZER_KWARGS
        })
        meta = getattr(cls, 'Meta', None)
        list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
        return list_serializer_class(*args, **list_kwargs)

当many=True,instance为Queryset对象

		ser = UserInfoSerializer(instance=users, many=True)
        print(ser.data)

最终ser.data会通过ListSerializer类中的to_representation返回

class ListSerializer(BaseSerializer):
    def to_representation(self, data):
        """
        List of object instances -> List of dicts of primitive datatypes.
        """
        # Dealing with nested relationships, data can be a Manager,
        # so, first get a queryset from the Manager if needed
        iterable = data.all() if isinstance(data, models.Manager) else data
"""通过迭代每一个queryset对象.ser.data返回如下类似
[OrderedDict([('id', 1), ('username', 'zhengshuang'), ('password', '123'), 
('type', '普通用户'), ('group', '1'), ('rls', [{'title': '1', 'id': 1)]数据"""
        return [
            self.child.to_representation(item) for item in iterable
        ]

当many=False,instance为类似<class ‘oldboy.models.UserInfo’>模型类的对象

ser = UserInfoSerializer(instance=users, many=True)
        print(ser.data)

最终ser.data会通过ModelSerializer(因为UserInfoSerializer继承serializers.ModelSerializer)类中的to_representation返回

class ModelSerializer(Serializer):
    def to_representation(self, instance):
        """
        Object instance -> Dict of primitive datatypes.
        """
        ret = OrderedDict()
        fields = self._readable_fields

        for field in fields:
            try:
                attribute = field.get_attribute(instance)
            except SkipField:
                continue

            # We skip `to_representation` for `None` values so that fields do
            # not have to explicitly deal with that case.
            #
            # For related fields with `use_pk_only_optimization` we need to
            # resolve the pk value.
            check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
            if check_for_none is None:
                ret[field.field_name] = None
            else:
                ret[field.field_name] = field.to_representation(attribute)

        return ret

if ser.is_valid():中is_valid()简单分析

class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
    def to_internal_value
                            
                            版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_44633951/article/details/104292500
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢