• Django Rest framework 之 序列化


    一、前言

    先建立数据库,并添加相应的数据,用来后面序列化使用

    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("UserGroup", on_delete=models.CASCADE)
        roles = models.ManyToManyField("Role") 
    
    
    class UserToken(models.Model):
        user = models.OneToOneField(to='UserInfo', on_delete=models.CASCADE)
        token = models.CharField(max_length=64)
    
    
    class Role(models.Model):
        title = models.CharField(max_length=32)
    

    并执行数据库迁移操作

    python manage.py makemigrations
    python manage.py migrate
    

    2、添加少量数据

    当数据迁移执行之后,会在sqlite数据库中生活如下表

    多对多关系的时候,django自动生成第三张表维系表关系,字段分别是userinforoleid,其中api_userinfo_roles为多对多关系生成的表。
    在表中添加少量数据

    <1>、UserInfo表

    <2>、UserGroup表

    3、UserInfo_roles表

    4、roles表

    二、序列化的简单使用

    1、不使用序列化

    <1>、路由

    from django.conf.urls import url
    
    from .views import RoleView
    
    urlpatterns = [
        url(r'^role/$', RoleView.as_view()),
    ]
    

    <2>、视图

    from rest_framework.views import APIView
    
    from .models import Role
    
    import json 
    
    class RoleView(APIView):
    
        def get(self, request, *args, **kwargs):
    
            roles = Role.objects.all().values('id' ,'title')
            print(roles, type(roles))  # roles为一个QuerySet对象
            ret_roles = json.dumps(list(roles), ensure_ascii=False)  # 多条数据
            return HttpResponse(ret_roles)
    

    2、简单使用Serializer

    <1>、定义序列化类

    from rest_framework import serializers
    
    class RoleSerializer(serializers.Serializer):
        id = serializers.IntegerField()
        title = serializers.CharField()
    

    <2>、视图

    class RoleView(APIView):
    
        def get(self, request, *args, **kwargs):
    
            # 多条数据
            # 将序列化后的数据都存到ser.data(OrderDict有序字典中)中
            # roles = Role.objects.all()
            # ser_roles = RoleSerializer(instance=roles, many=True)
            # print(ser_roles, type(ser_roles))  # ListSerializer对象
            # ret_roles = json.dumps(ser_roles.data, ensure_ascii=False)  # 多条数据
            # return HttpResponse(ret_roles)
    
            # 单条数据
            role = Role.objects.all().first()
            ser_role = RoleSerializer(instance=role, many=False)  # RoleSerializer对象
            print(ser_role, type(ser_role))  # 单条数据
            ret_roles = json.dumps(ser_role.data, ensure_ascii=False)
            return HttpResponse(ret_roles)
    

    总结:上面可以实现数据的简单序列化,但是无法自定义字段,也无法对数据进行处理,不方便,限制较大

    三、进一步使用Serializer

    1、路由

    from django.conf.urls import url
    
    from .views import UserInfo
    
    urlpatterns = [
        url(r'^userinfo/$', UserInfo.as_view()),
    ]
    

    2、视图

    class UserInfoView(APIView):
    
        def get(self, request, *args, **kwargs):
    
            users = UserInfo.objects.all()
            users_ser = UserSerializer(instance=users, many=True)
            users_ret = json.dumps(users_ser.data, ensure_ascii=False)
            # print(users_ser.data, type(users_ser.data), type(users_ser.data[0]))
            return HttpResponse(users_ret)
    
    

    3、使用serializer

    class UserSerializer(serializers.Serializer):
        type = serializers.IntegerField(source='user_type')
        user_type = serializers.CharField(source='get_user_type_display')  # choices字段显示
        username = serializers.CharField()
        pwd = serializers.CharField(source='password')  # 自定义serializer中的key值
        group_title = serializers.CharField(source='group.title')  # 关联对象属性
        roles = serializers.CharField(source='roles.all')  # 多对多关系
        roles_info = serializers.SerializerMethodField()   # 表示自定义方法,显示querytset对象详情
    
        def get_roles_info(self, row):
            roles = row.roles.all()
            ret = []
            for item in roles:
                ret.append(
                    {
                        'id': item.id,
                        'title': item.title
                    }
                )
            return ret
    

    • 如果没有指定在Filed中没有定义source参数的时候,就自动与数据库modles定义的字段进行匹配,如上面的userrname字段。在定义字段后,Serializer类中可以自定义属性如type
    • models中是以choice定义时:需要定义source参数定义get_字段名_display才能获取数据,这与在模板语言中的用法一样,如上面的user_type
    • 外键关联的时候,直接 外键字段名.属性 的方式定义传参给source参数即可,如上面的group.title
    • 对于roles字段,想直接获取所有的对象,但是无法做到细粒度的将对象的所有属性展示出来,只能获取到QuerySet对象
    • 自定义字段,处理数据,如roles_info获取所有的role对象的属性,处理数据可以定义方法,方法名格式为get_属性,并return值最终返回值

    执行结果:

    :自定义字段也可以采取继承的方式,如:

    class UsernameField(serializers.CharField):
        def to_representation(self, value):
            return 'username' + value
    

    重写to_representation方法,value为从数据库取出的值,然后对value进行处理,在返回即可
    并将序列化类中的username改为
    username = UsernameField()

    四、使用ModelSerializer组件

    1、包装Serializer

    
    class UserSerializer(serializers.ModelSerializer):
        user_type = serializers.CharField(source='get_user_type_display')
        roles = serializers.CharField(source='roles.all')  # 外键关联
        roles_info = serializers.SerializerMethodField()   # 表示自定义方法,显示外键关联详情
        group_title = serializers.CharField(source='group.title')
        def get_roles_info(self, row):
            roles = row.roles.all()
            ret = []
            for item in roles:
                ret.append(
                    {
                        'id': item.id,
                        'title': item.title
                    }
                )
            return ret
    
        class Meta:
            model = UserInfo
            # fields = '__all__'  # 为全部的字段做匹配
            fields = ['user_type', 'username', 'password', 'group', 'group_title', 'roles', 'roles_info']  # 自定义需要展示的字段
            extra_kwargs = {'group': {'source': 'group_id'}}
    

    ModelSerializerSerializer区别在于ModelSerializer支持了Serializer中所有的操作,并且通过自动生成所有数据字段与序列化类的一一对应关系,而不用自己手动添加。
    SerializerModelSerializer的父类,所以ModelSerializer才会支持Serializer的所有操作

    返回结果

    2、ModelSerializer深度控制

    在上面,看到在进行连表查询的时候,只能获取到外键关联对象,在当前表中存储的id,怎样拿到外键关联对象的具体信息。

    class UserSerializer(serializers.ModelSerializer):
        # 自动向内部进行深度查询  depth表示查询层数
        class Meta:
            model = UserInfo
            # fields = "__all__"
            fields = ['id','username','password','group','roles']
            depth = 1 # 0 ~ 10  默认的depth为0
    
    class UserInfoView(APIView):
    
        def get(self, request, *args, **kwargs):
    
            users = UserInfo.objects.all()
            users_ser = UserSerializer(instance=users, many=True)
            users_ret = json.dumps(users_ser.data, ensure_ascii=False)
            # print(users_ser.data, type(users_ser.data), type(users_ser.data[0]))
            return HttpResponse(users_ret)
    

    :这里的depth就表示深度查询的层数,默认的层数为0,层数越多查询效率越慢。
    返回结果

    3、自动生成链接

    在上面我们看到,在返回组group的时候是返回该组的id,或者用depth深度控制,返回组的详细信息。在restful规范中,规定应该给出相应的详情链接,可以通过url拼接,在django rest framework中也有相对应的实现。
    首先改写一下用户信息序列化类,使之能够提供用户组详情的有关url

    class UserSerializer(serializers.ModelSerializer):
        group = serializers.HyperlinkedIdentityField(view_name='api:gp', lookup_field='group_id', lookup_url_kwarg='xxx')  
        # view_name参数 进行传参的时候是参考路由匹配中的name与namespace参数
        #  lookeup_field参数是根据在UserInfo表中的连表查询字段group_id
        # look_url_kwarg参数在做url反向解析的时候会用到
    
        class Meta:
            model = UserInfo
            fields = ['id','username','password','group','roles']
            depth = 1 # 0 ~ 10
    
    
    class UserInfoView(APIView):
    
        def get(self, request, *args, **kwargs):
    
            users = UserInfo.objects.all()
            users_ser = UserSerializer(instance=users, many=True, context={'request': request})  # 在做链接的时候需要添加context参数
            users_ret = json.dumps(users_ser.data, ensure_ascii=False)
            # print(users_ser.data, type(users_ser.data), type(users_ser.data[0]))
            return HttpResponse(users_ret)
    
    
    # 添加group序列化类
    class GroupSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = UserGroup
            fields = "__all__"
    
    # 返回用户组的详细信息
    class GroupView(APIView):
        def get(self,request,*args,**kwargs):
            pk = kwargs.get('xxx')
            obj = UserGroup.objects.filter(pk=pk).first()
    
            ser = GroupSerializer(instance=obj,many=False)
            ret = json.dumps(ser.data,ensure_ascii=False)
            return HttpResponse(ret)
    

    返回结果

    当我们点解用户组详情链接后,返回结果

    4、校验数据

    序列化不仅可以做数据的返回,也可以对前端提交的数据进行校验。

    <1>、类方法检验

    class TitleValidator(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)
    
        def set_context(self, serializer_field):
            # 执行验证之前调用,serializer_fields是当前字段对象
            pass
    
    
    class UserGroupSerializer(serializers.Serializer):
        title = serializers.CharField(error_messages={'required': '标题不能为空'}, validators=[TitleValidator('Django'),])
    
    
    class UserGroupView(APIView):
    
        def post(self,request,*args,**kwargs):
    
            print(request.data)
            ser = UserGroupSerializer(data=request.data)
            if ser.is_valid():
                print(ser.validated_data['title'])
            else:
                print(ser.errors)
    
            return HttpResponse('提交数据')
    

    上面的TitileValidator类封装了对request.data前端传来的数据的校验,title相对应的是数据中的keytitle的值。TitileValidator实现了call()特殊方法,并把具体的验证逻辑封装到里边,是一个可直接调用的对象。而self.base则为具体的title对应的数据,进行处理。

    <2>、钩子方法

    class UserGroupSerializer(serializers.Serializer):
    
        title = serializers.CharField()
    
        def validate_title(self, value):
            from rest_framework import exceptions
            if not value:
               raise exceptions.ValidationError('不可为空')
            return value
    
    class UserGroupView(APIView):
    
        def post(self,request,*args,**kwargs):
    
            print(request.data)
            ser = UserGroupSerializer(data=request.data)
            if ser.is_valid():
                print(ser.validated_data['title'])
            else:
                print(ser.errors)
    
            return HttpResponse('提交数据')
    

    在定义钩子方法的时候,钩子函数是以validate_字段名的方式进行命名的。只有遵循这样的格式,在Serializer内部会对钩子函数的名字进行拆分并识别出来。在validate_title内部封装了对数据的校验操作,value则为具体的值

  • 相关阅读:
    linux中jdk的安装
    @Transactional 的回滚
    监听器以及在监听类里面获得bean的方法
    list的顺序反转
    iOS 升级到xcode 7和iOS9 问题处理
    iOS 9.0系统策略更新
    iOS 技术分享3
    iOS 技术分享2
    iOS 技术分享
    iOS 面试题
  • 原文地址:https://www.cnblogs.com/welan/p/10151714.html
Copyright © 2020-2023  润新知