• 序列化


    普通的 django 序列化

    # demoapiurls.py
    
    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/roles/$', views.RolesView.as_view()),
    ]
    
    # demoapiviews.py
    
    class RolesView(APIView):
        def get(self, request, *args, **kwargs):
    
            # 方式一
            roles = models.Role.objects.all().values("id", "title")
            roles = list(roles)
            ret = json.dumps(roles, ensure_ascii=False)
            
            return HttpResponse(ret)
    

    对 QuerySet 数据进行序列化

    # demoapiviews.py
    
    # 方式二:多条数据的 QuerySet
    from rest_framework import serializers
    
    class RolesSerializer(serializers.Serializer):
        # 必须和数据库中的字段一致
        id = serializers.IntegerField()
        title = serializers.CharField()
    
    class RolesView(APIView):
        def get(self, request, *args, **kwargs):
            
            roles = models.Role.objects.all()   # QuerySet类型, 有多条数据
            # 实例化一个RolesSerializer对象, many=True表示处理多条数据
            ser = RolesSerializer(instance=roles, many=True)
            # ensure_ascii=False 表示不处理中文字符
            ret = json.dumps(ser.data, ensure_ascii=False)
    
            return HttpResponse(ret)
    

    # demoapiviews.py
    
    # 方式二:一条数据的 QuerySet
    from rest_framework import serializers
    
    class RolesSerializer(serializers.Serializer):
        # 必须和数据库中的字段一致
        id = serializers.IntegerField()
        title = serializers.CharField()
    
    class RolesView(APIView):
        def get(self, request, *args, **kwargs):
            
            role = models.Role.objects.all().first()
            ser = RolesSerializer(instance=role, many=False)	# many=False,一条数据
            ret = json.dumps(ser.data, ensure_ascii=False)
    
            return HttpResponse(ret)
    

    序列化自定义字段

    # demoapiviews.py
    
    from rest_framework import serializers
    
    class UserInfoSerializer(serializers.Serializer):
        username = serializers.CharField()
        password = serializers.CharField()
    
    class UserInfoView(APIView):
        def get(self, request, *args, **kwargs):
    
            users = models.UerInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
    

    现在要显示用户类型,只需要添加 user_type 字段即可,或者使用 source ,source 也是对应数据库的字段

    # demoapiviews.py
    
    from rest_framework import serializers
    
    class UserInfoSerializer(serializers.Serializer):
        # 使用了source,前面的字段就不能和数据库字段相同了
        xxx = serializers.CharField(source="user_type")
        username = serializers.CharField()
        password = serializers.CharField()
    
    class UserInfoView(APIView):
        def get(self, request, *args, **kwargs):
    		
            users = models.UerInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
    

    但是这样显示的只是用户类型的 id 字段,若想显示用户类型对应的中文,则需要使用 get_???_display ,与前面学习 django 所不同的是,这里的 display 并不需要加上括号

    # demoapiviews.py
    
    from rest_framework import serializers
    
    class UserInfoSerializer(serializers.Serializer):
        # 使用了source,前面的字段就不能和数据库字段相同了
        xxx = serializers.CharField(source="user_type")
        yyy = serializers.CharField(source="get_user_type_display")
        username = serializers.CharField()
        password = serializers.CharField()
    
    class UserInfoView(APIView):
        def get(self, request, *args, **kwargs):
    		
            users = models.UerInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
    

    为什么 display 不用加括号?

    ​ 因为源码中会做一个判断,如果拿到的是数据库的字段,直接通过点语法在数据库中取值,如果是 get_???_display ,则会自动加上括号,自动执行

    models.py 中的 UerInfo 类有一个 group = models.ForeignKey("UserGroup") ,显示用户所在的组,可以通过点的语法,并且如果后面还有字段,可以一直点下去

    # demoapiviews.py
    
    from rest_framework import serializers
    
    class UserInfoSerializer(serializers.Serializer):
        # 使用了source,前面的字段就不能和数据库字段相同了
        # xxx = serializers.CharField(source="user_type")
        yyy = serializers.CharField(source="get_user_type_display")
        username = serializers.CharField()
        password = serializers.CharField()
        # gp = serializers.CharField(source='group')	# 这里取到的只是组的对象
        gp = serializers.CharField(source='group.title')
    
    class UserInfoView(APIView):
        def get(self, request, *args, **kwargs):
    		
            users = models.UerInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
    

    罗列当前用户所有的角色

    # demoapiviews.py
    
    from rest_framework import serializers
    
    class UserInfoSerializer(serializers.Serializer):
        # 使用了source,前面的字段就不能和数据库字段相同了
        # xxx = serializers.CharField(source="user_type")
        yyy = serializers.CharField(source="get_user_type_display")
        username = serializers.CharField()
        password = serializers.CharField()
        # gp = serializers.CharField(source='group')	# 这里取到的只是组的对象
        gp = serializers.CharField(source='group.title')
        rls = serializers.CharField(source='roles.all')	# 取到所有的角色对象
    
    class UserInfoView(APIView):
        def get(self, request, *args, **kwargs):
    		
            users = models.UerInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
    

    上面取到的是所有的角色对象,并不能取出角色对象和 id 的详细信息。所以有另一种方法

    class UserInfoSerializer(serializers.Serializer):
        # source指的是对应数据库的字段
        # xxx = serializers.CharField(source="user_type")
        yyy = serializers.CharField(source="get_user_type_display")
        username = serializers.CharField()
        password = serializers.CharField()
        gp = serializers.CharField(source='group.title')
        # rls = serializers.CharField(source='roles.all')
        rls = serializers.SerializerMethodField()       # 自定义显示
    
        # 自定义显示需要有自定义函数, 函数名是get_字段, 参数是当前UserInfo这一行的对象
        def get_rls(self, row):
            # 取出关联的所有角色, 赋给每一个对象
            role_obj_list = row.roles.all()
            ret = []
            # 取出每一个对象中指定的值, 添加到返回值
            for item in role_obj_list:
                ret.append({"id": item.id, "title": item.title})
    
            # 返回值会赋给 rls
            return ret
        
    class UserInfoView(APIView):
        def get(self, request, *args, **kwargs):
    
            users = models.UerInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
    

    ModelSerializer

    class UserInfoSerializer(serializers.ModelSerializer):
        # 也支持自定义字段,或者自定义函数显示
        yyy = serializers.CharField(source="get_user_type_display")
    
        class Meta:
            model = models.UerInfo	# 根据数据库的对应关系自动生成指定的字段
            # fields = "__all__"	# 获取所有的字段,但只是很简陋的信息
            fields = ['id', 'username', 'password', 'yyy']
            dept = 3	# 深度
            
    class UserInfoView(APIView):
        def get(self, request, *args, **kwargs):
    
            users = models.UerInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
    

    部分总结

    写类

    class UserInfoSerializer(serializers.Serializer):
        username = serializers.CharField()
        password = serializers.CharField()
    
    class UserInfoSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UerInfo
            fields = "__all__"
            fields = ['id', 'username', 'password', ]
    

    字段

    title = serializers.CharField(source="x.xx.xxx")
    
    title = serializers.SerializerMethodField()
    def get_title(self, xxx):
    	do something..
        return xx	# 返回值会交给 title
    

    自定义类(不常用)

    class MyField(serializers.CharField):
    
        def to_representation(self, value):
            print(value)    # value相当于从数据库中取到的字段
            return 'xxx'    # 返回值在页面中显示
    
    class UserInfoSerializer(serializers.ModelSerializer):
        x1 = MyField(source='username')
        yyy = serializers.CharField(source="get_user_type_display")
    
        class Meta:
            model = models.UerInfo
            # fields = "__all__"
            fields = ['id', 'username', 'password', 'yyy', 'group', 'x1']
    
    class UserInfoView(APIView):
        def get(self, request, *args, **kwargs):
    
            users = models.UerInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
    

    深度控制

    class UserInfoSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = models.UerInfo
            fields = ['id', 'username', 'password', 'group', 'roles']
            depth = 1   # 深度, 表示展现的层级(官方建议不超过10层, 个人建议不超过3层)
    
    
    class UserInfoView(APIView):
        def get(self, request, *args, **kwargs):
    
            users = models.UerInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
    
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
    

    生成链接

    现在将 depth 设为 0,group 显示的是 id,现在想将 group 设置为一个 URL,让它点击才能查看详情

    首先需要添加这个 URL 配置到路由中

    url(r'^(?P<version>[v1|v2]+)/group/(?P<pk>d+)$', views.GroupView.as_view(), name='gp'),
    

    然后在 UserInfoSerializer 中,将 groupid , 反向生成 URL,并在序列化的时候添加 context

    class UserInfoSerializer(serializers.ModelSerializer):
    
        # 将 group的id, 反向生成URL
        group = serializers.HyperlinkedIdentityField(view_name='gp')
    
        class Meta:
            model = models.UerInfo
            fields = ['id', 'username', 'password', 'group', 'roles']
            depth = 0   # 深度, 表示展现的层级(官方建议不超过10层, 个人建议不超过3层)
    
    class UserInfoView(APIView):
        def get(self, request, *args, **kwargs):
    
            users = models.UerInfo.objects.all()
            # 添加 context
            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__"
            depth = 0
    
    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)
    

    可以看到 group 成为一个链接,但又出现一个新的问题,group 中数据库中并没有第 2 组(实际上这里 group 默认用的是上面的 id ),所以需要改进

    class UserInfoSerializer(serializers.ModelSerializer):
    
        # 将 group的id, 反向生成URL
        # 添加两个参数, lookup_url_kwarg 要与路由中的有名分组相同
        # 路由: url(r'^(?P<version>[v1|v2]+)/group/(?P<pk>d+)$', views.GroupView.as_view(), name='gp'),
        group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='pk')
    
        class Meta:
            model = models.UerInfo
            fields = ['id', 'username', 'password', 'group', 'roles']
            depth = 0   # 深度, 表示展现的层级(官方建议不超过10层, 个人建议不超过3层)
    
    
    class UserInfoView(APIView):
        def get(self, request, *args, **kwargs):
    
            users = models.UerInfo.objects.all()
            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__"
            depth = 0
    
    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)
    
    

    点击 group 的链接,也可以跳转到相应详情页面,不过这种情况用的不多

    源码流程

    ModelSerializer

    class UserInfoSerializer(serializers.ModelSerializer):
    
        # 将 group的id, 反向生成URL
        group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='pk')
    
        class Meta:
            model = models.UerInfo
            fields = ['id', 'username', 'password', 'group', 'roles']
            depth = 0   # 深度, 表示展现的层级(官方建议不超过10层, 个人建议不超过3层)
    
    
    class UserInfoView(APIView):
        def get(self, request, *args, **kwargs):
    
            users = models.UerInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True, context={'request': request})
    
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
    

    首先是创建类 UserInfoSerializerser = UserInfoSerializer(instance=users, many=True, context={'request': request}) 对其进行实例化,在一个类进行实例化的时候,会执行 __init__ 方法,在执行 __init__ 之前,会执行 __new__ 方法。现在去寻找这两个方法,自己没有就寻找父类。

    UserInfoSerializer --> ModelSerializer --> Serializer --> BaseSerializer,在 BaseSerializer 中发现有这两个方法

    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(BaseSerializer, self).__init__(**kwargs)
    
    
    def __new__(cls, *args, **kwargs):
        # We override this method in order to automagically create
        # `ListSerializer` classes instead when `many=True` is set.
        # 拿取many参数,没有就是False
        if kwargs.pop('many', False):
            # 如果为True,执行这一句,即many=True,表示对QuerySet进行处理
            # 执行 many_init 方法
            return cls.many_init(*args, **kwargs)
        # 如果为False,执行这一句,即many=False,表示对对象进行处理
        # 执行父类的 __new__ 方法,返回一个对象,这个对象返回完之后,其实就是 __init__ 中实例化的对象
        # 接着执行 __init__ 方法
        return super(BaseSerializer, cls).__new__(cls, *args, **kwargs)
    
    @classmethod
    def many_init(cls, *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)
        # 去meta中读取 list_serializer_class 参数,没有就使用默认的 ListSerializer 参数
        '''
        如果是多个 QuerySet对象,看到的是Serializer进行处理,但Serializer内部调用ListSerializer处理
        上面many=True,所以这里使用ListSerializer类处理,实例化的是 ListSerializer 类的对象,接着执行该对象的 __init__ 方法
        如果many=False,则使用BaseSerializer类处理,实例化 BaseSerializer 类的对象,执行该对象的 __init__ 方法
        '''
        list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
        return list_serializer_class(*args, **list_kwargs)
    

    这时实例化的步骤完成,ser = UserInfoSerializer(instance=users, many=True, context={'request': request})users 要么是 QuerySet 对象,要么是一个普通的对象,接着 ret = json.dumps(ser.data, ensure_ascii=False) ,这里调用了 ser.data

    @property
    def data(self):
        # 执行父类的data属性,去查看一下
        ret = super(Serializer, self).data
        # ReturnDict,封装有序字典
        return ReturnDict(ret, serializer=self)
    

    这里有个 self.to_representation ,回到实例化之后,无论是普通对象还是 QuerySet 对象,都会执行这个方法,来查看一下 to_representation 做了什么

    可以看到它提供了很多 to_representation ,所以不应该这样查找,应该从自己所写的 UserInfoSerializer 类中开始查找

    UserInfoSerializer --> ModelSerializer --> Serializer,在 Serializer 类中发现 to_representation

    def to_representation(self, instance):
        """
        Object instance -> Dict of primitive datatypes.
        """
        # 有序字典
        ret = OrderedDict()
        fields = self._readable_fields
    	
        # fields相当于定义和数据库生成的字段
        for field in fields:
            try:
                # 调用字段(CharField)的get_attribute方法
                # 这个instance就是最开始传入的对象
                # 这个对象可以使用点方法,比如 对象.username
                attribute = field.get_attribute(instance)	# 可以看一下这个get_attribute
            except SkipField:
                continue
            ...
    

    通过序列化字段的 CharField 来查找 get_attribute,CharField --> Field,在 Field 中找到 get_attribute

    # instance是最开始传入的对象
    def get_attribute(self, instance):
        """
        Given the *outgoing* object instance, return the primitive value
        that should be used for this field.
        """
        try:
            # source_attrs是在写序列化自定义字段时传入的source组成的列表,
            # 例如 group.title,get_user_type_display,roles.all
            # source_attrs里面执行了 source.split('.'),把所有的通过点进行分割
            # instance是最开始传入的对象
            return get_attribute(instance, self.source_attrs)	# 再来看一下这个get_attribute
        ...
    
    # 传入上面的参数,attrs就是 self.source_attrs,如果是 group.title
    # 这里attrs就是[group, title]
    def get_attribute(instance, attrs):
        for attr in attrs:
            try:
                if isinstance(instance, collections.Mapping):
                    instance = instance[attr]
                else: 
                    instance = getattr(instance, attr)
            except ObjectDoesNotExist:
                return None
            # 如果是 get_user_type_display
            # is_simple_callable内部判断了是方法还是函数
            if is_simple_callable(instance):
                try:
                    # display自动加括号
                    instance = instance()
                except (AttributeError, KeyError) as exc:
                    raise ValueError('Exception raised in callable attribute "{0}"; original exception was: {1}'.format(attr, exc))
    
        return instance
    

    请求数据校验

    class UserGroupSerializer(serializers.Serializer):
        title = serializers.CharField(error_messages={'required': '标题不能为空'})
    
    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('提交数据')
    

    自定义验证规则

    写一个标题必须以 "老男人" 开头

    class XXValidator(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(error_messages={'required': '标题不能为空'}, validators=[XXValidator('老男人'),])
    
    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('提交数据')
    

    但是一般并不这样使用,因为它有自己的钩子函数 (从 is_valid() 入手)

  • 相关阅读:
    开课 博客
    给定数组求数组中和最大子数组的和
    课堂测验
    读梦断代码有感(3)2019.2.20
    读梦断代码有感(2)2019.2.10
    读梦断代码有感(1)2019.2.05
    进度七
    进度 六
    sjz地铁作业
    进度四
  • 原文地址:https://www.cnblogs.com/qiuxirufeng/p/10517336.html
Copyright © 2020-2023  润新知