zoukankan      html  css  js  c++  java
  • Django序列化组件Serializers详解

      本文主要系统性的讲解django rest framwork 序列化组件的使用,基本看完可以解决工作中序列化90%的问题,写作参考官方文档https://www.django-rest-framework.org/api-guide/serializers/#modelserializer,分成如下九个部分:

      01、为什么要用序列化组件

      02、序列化组件的基本使用

      03、序列化组件常用字段

      04、序列化组件is_valid、validated_data

      05、序列化组件校验字段 

      06、序列化组件.create() and .update()

      07、序列化组件ModelSerializer

      08、序列化组件构造复杂的结构

      09、序列化组件修改返回值to_representation、to_internal_value

    01、为什么要用序列化组件

      我们知道前后端常用json数据结构交互, 在后端我们常想把一个对象返回给前端,但是json序列化是不能序列化对象(不过可以添加序列化参数encoder序列化原理和序列化组件差不多需要自己定义序列化类和返回的结构),所以就有了我们的序列化组件,可以自定义特定结构把对象序列化返回给前端,同时可以对前端传入的参数进行数据校验等功能。

    02、序列化组件的基本使用

    models

    from django.db import models
    
    # Create your models here.
    
    
    class Book(models.Model):
        id = models.IntegerField(primary_key=True)
        title = models.CharField(max_length=255)
        desc = models.CharField(max_length=255)
        is_deleted = models.IntegerField(choices=[(1, "删除"), (0, "未删除")])
        author = models.CharField(max_length=255)

    serializer

    from rest_framework.serializers import Serializer
    from rest_framework import serializers
    
    
    class BookSerializer(Serializer):
        id = serializers.IntegerField()
        title = serializers.CharField()
        desc = serializers.CharField()
        is_deleted = serializers.ChoiceField(choices=[(1, "删除"), (0, "未删除")], source="get_is_deleted_display")
        author = serializers.CharField()

    views

    from app01.models import Book
    from app01.serializer import BookSerializer
    from django.http import HttpResponse, JsonResponse
    
    # Create your views here.
    
    
    def get_books(request):
        books = Book.objects.all()
        se = BookSerializer(books, many=True)
        return JsonResponse(se.data, safe=False)

      结果返回:

    [{"id": 1, "title": "活着", "desc": "讲述一代人的人生", "is_deleted": "未删除", "author": "余华"}]

      在写法上model和serializer的写法非常相近,但内在逻辑model是与数据库表的关系映射,serializer是对对象的序列化和反序列化。

    03、序列化组件常用字段

    常用字段类型

    字段字段构造方式
    BooleanField BooleanField()
    NullBooleanField NullBooleanField()
    CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
    EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
    RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
    SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
    URLField URLField(max_length=200, min_length=None, allow_blank=False)
    UUIDField UUIDField(format='hex_verbose')
    format:
    1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
    2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a"
    3)'int' - 如: "123456789012312313134124512351145145114"
    4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
    IPAddressField IPAddressField(protocol='both', unpack_ipv4=False, **options)
    IntegerField IntegerField(max_value=None, min_value=None)
    FloatField FloatField(max_value=None, min_value=None)
    DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None)
    max_digits: 最多位数
    decimal_palces: 小数点位置
    DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
    DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
    TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
    DurationField DurationField()
    ChoiceField ChoiceField(choices)
    choices与Django的用法相同
    MultipleChoiceField MultipleChoiceField(choices)
    FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
    ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
    ListField ListField(child=, min_length=None, max_length=None)
    DictField DictField(child=)

     

    选项参数:

    名称作用
    max_length 最大长度
    min_lenght 最小长度
    allow_blank 是否允许为空
    trim_whitespace 是否截断空白字符
    max_value 最大值
    min_value 最小值

    通用参数

    参数名称说明
    read_only 表明该字段仅用于序列化输出,默认False
    write_only 表明该字段仅用于反序列化输入,默认False
    required 表明该字段在反序列化时必须输入,默认True
    default 反序列化时使用的默认值
    allow_null 表明该字段是否允许传入None,默认False
    validators 该字段使用的验证器
    error_messages 包含错误编号与错误信息的字典
    label 用于HTML展示API页面时,显示的字段名称
    help_text 用于HTML展示API页面时,显示的字段帮助提示信息


     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     

      这一段引用自https://www.jianshu.com/p/26529651aa27

      在这里额外讲一个参数source,在官方文档中是这样解释的:

    将用于填充字段的属性的名称。可以是仅接受self参数的方法,例如URLField(source='get_absolute_url'),也可以使用点分符号遍历属性,例如EmailField(source='user.email')。当使用点分符号序列化字段时,default如果在属性遍历期间任何对象不存在或为空,则可能需要提供一个值。

    该值source='*'具有特殊含义,用于指示应将整个对象传递给该字段。这对于创建嵌套表示或对需要访问完整对象才能确定输出表示的字段很有用。

    默认为字段名称。

      其中比较常用的用法:

      1、source="get_field_name_display"如上所展示,在choice字段中可以展示选项对应的解释,这中用法代表的是官方解释说的可以是仅接受self参数的方法,也就是source中可以填serializer对象可以调用的方法(方法中需要传入self),内在逻辑是这个字段会展示此方法的返回的结果。

      2、source='user.email'这种用法常在ModelSerializer的子类中,其中user为User对象,此中写法是指展示user的email属性,常用于我们想把外键对象user的属性和本对象的属性展示在同一层级,而不是下一级。

    04、序列化组件is_valid、validated_data

      当我们定义好序列化器时,怎么校验传入的字段呢?那就是is_valid方法,可以根据定义序列化器的校验规则判断传入字段是否合法。

    serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
    serializer.is_valid()
    # False
    serializer.errors
    # {'email': ['Enter a valid e-mail address.'], 'created': ['This field is required.']}

      然后serializer.validated_data就可以获取校验过后的数据字典。

    05、序列化组件校验字段 

      序列化组件校验字段的方式常有三种:

      1、首先是在字段的validators属性, 其中传入一个校验方法列表如:validators=(my_validator, )其中my_validator中定义校验规则。

    def multiple_of_ten(value):
        if value % 10 != 0:
            raise serializers.ValidationError('Not a multiple of ten')
    
    class GameRecord(serializers.Serializer):
        score = IntegerField(validators=[multiple_of_ten])

      2、最常用的是定义一个validate_field_name(self, value)的函数(其中field_name指的是字段名),函数内是具体的逻辑。

    from rest_framework import serializers
    
    class BlogPostSerializer(serializers.Serializer):
        title = serializers.CharField(max_length=100)
        content = serializers.CharField()
    
        def validate_title(self, value):
            """
            Check that the blog post is about Django.
            """
            if 'django' not in value.lower():
                raise serializers.ValidationError("Blog post is not about Django")
            return value

      3、最后一种是定义一个validate(self, data)其中data是所有字段的键值对,所以这个校验方法是对象级别的校验。

    from rest_framework import serializers
    
    class EventSerializer(serializers.Serializer):
        description = serializers.CharField(max_length=100)
        start = serializers.DateTimeField()
        finish = serializers.DateTimeField()
    
        def validate(self, data):
            """
            Check that start is before finish.
            """
            if data['start'] > data['finish']:
                raise serializers.ValidationError("finish must occur after start")
            return data

      当然或许当你看到这里会问why?how? 只能说源码是最好的答案。

    06、序列化组件.create() and .update()

      在我们定义的序列化类中, 可以添加create和update方法,当我们有需求是根据反序列化后的数据在数据库表中创建记录或者更新某条数据,这时我们就可以在create方法和update方法中定义对应的逻辑。

    class CommentSerializer(serializers.Serializer):
        email = serializers.EmailField()
        content = serializers.CharField(max_length=200)
        created = serializers.DateTimeField()
       def create(self, validated_data):
            return Comment.objects.create(**validated_data)
    
        def update(self, instance, validated_data):
            instance.email = validated_data.get('email', instance.email)
            instance.content = validated_data.get('content', instance.content)
            instance.created = validated_data.get('created', instance.created)
            instance.save()
            return instance
    
    

      接下来调用serializer.save()命令便可创建一条纪录或者更新一条记录,其中判断save时什么时候是创建什么时候是更新呢?关键在于serializer的实例化。

    # .save() will create a new instance.
    serializer = CommentSerializer(data=data)
    serializer.save()
    # .save() will update the existing `comment` instance.
    serializer = CommentSerializer(comment, data=data)

    serializer.save()

      其中当Serializer类实例化没有传入model对象时会调用create方法创建一条记录, 如果Serializer类实例化时传入了model对象就会调用update方法更新一条记录。

      有时除了反序列化的字段我们还需要其他字段怎么办呢?我们可以在save中传入参数名和值,可以在validated_data中根据参数名取到对应的值。

    serializer.save(owner=request.user)

      这样我们就可以在validated_data.get("owner")就可以取到user对象了。

    07、序列化组件ModelSerializer

      ModelSerializer和表单的ModelForm组件很相似,都极大简化了我们的开发,可以在内部类Meta中定义对应的model,ModelSerializer就会自动生成model字段对应的Field不用我们定义。

    class AccountSerializer(serializers.ModelSerializer):
        class Meta:
            model = Account
            fields = ['id', 'account_name', 'users', 'created']

       其中fields中定义序列化的字段,如果是全部字段就写__all__, 上述例子我们没有定义具体的字段,ModelSerializer帮我们自动生成了 'id', 'account_name', 'users', 'created'的Field。

    08、序列化组件构造复杂的结构

      以下是个存在一对多和多对多字段的序列化器

    serializer

    from res_framework import serializers
    
    #这个类用于被实例化,多对多字段这么写
    class AuthorSerializer(serializers.Serializer):
        id = serializers.Charfield()
        name = serializers.Charfield()
        age = serializers.Charfield()
    
    #传给views.py的主类
    class BookSerializer(serializers.Serializer):
        name = serializers.Charfield()
        #source 可以指定字段 ,  id是要序列化的表名。
        id  = serializers.CharField(source='nid') 
        #,source后字段用.的方式可以跨表查询。
        publish = serializer.CharField(source='publish.email')
        ''' 
        如果在models.py的book类中定义一个test方法。
        def test(self): 
            return str(self.price)+self.name
       '''
       # 返回的结果就会有xx字段,souce不但可以指定表模型字段,还可以指定模型表方法,并且赋值给xx变量
       xx = serializers.Charfield(source='test')
       
       
       #外键的实现方法:
       #一对多字段
       #如果要通过外键字段返回出版社的所有信息,包括id,name,email...
       #obj是当前循环序列化到的数据对象
        publish = serializers.SerializerMethodField()
        def get_publish(self,obj):
            return {’id‘:obj.publish.pk,'name':obj.publish.name}
       
       #多对多字段
       #所有作者的详情,也展示出来
        authors = serializers.SerializermethodFiled()
        def get_authors(self,obj):
            author_list  = obj.authors.all()
            author_ser = AuthorSerializer(author_list,many=True)
            return author_ser.data

    views

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from app01 import models
    
    class BookView(APIview):
        def get(self,request,*args,**kwargs):
                #获取所有图书数据
            response = {'status':100,'msg':'获取成功'}
            book_list = models.Book.objects.all()
            #实例化BookSerializer类,把要序列化的数据book_list传入
            #如果要序列化querySet对象,一定要加many = True
            book_ser = BookSerializer(book_list,many=True)
            #把序列化后的数据book_ser.data 拿出来放到response字典中返回给客户端
            response['data'] = book_ser.data 
            return Response(response)

    如果使用ModelSerializer:

    serializer

    from app01 import models
    class PublishSerializer(serializers.ModelSerializer): 
        class Meta: #固定写法
        # 指定要序列化Book表
        model = models.Book
        #指定要序列化的字段
        fields = ['nid','name']
        #序列化所有字段
        fileds ='__all__‘ 
        #要排除的字段(不能与fileds连用)
        # exclude = ['name','price'] 
        #深度判定
        depth = 1 
        #如果要不按照父类的来,想要自己定义显示的字段的话,自己定义一个,覆盖掉父类的字段属性。
        publish = serializers.SerializerMethodField()  #一对多字段
        def get_publish(self,obj):
            return {’id‘:obj.publish.pk,'name':obj.publish.name}

      此段摘自https://blog.csdn.net/qq_36019490/article/details/90339490

      这里额外解释一下depth这个参数:在对象外键另一对象,另一对象又外键另一对象以此类推,depth的作用就是可以决定序列化时的外键深度。

      复杂序列化器的要点在于,serializers.SerializerMethodField()和get_field_name的使用获取自己想得到的字段值,还有source的使用,上面有讲。

    09、序列化组件修改返回值to_representation、to_internal_value

      to_representation(self, instance):如果序列化器定义了此方法,可以改变序列化对象data的值,也就是serializer.data的值,你可以根据自己的业务场景去重新构造返回值。

    def to_representation(self, instance):
        """Convert `username` to lowercase."""
        ret = super().to_representation(instance)
        ret['username'] = ret['username'].lower()
        return ret

      to_internal_value(self, data): data为未经校验的数据字段, 此方法可以实现校验和修改反序列化后的值,然后返回。如果不想修改反序列化后的值只是做校验的话,完全可以使用validate方法替代。

    def to_internal_value(self, value):
        if value == None:
            return 0
        return value

      总而言之,这两个方法一个是用于重新构造validated_data并返回,一个用于重新构造serializer.data的值并返回。

  • 相关阅读:
    B轮公司技术问题列表(转)
    mysql函数之截取字符串
    谁才是真正的水果之王
    Mysql几种索引方式的区别及适用情况 (转)
    web安全之攻击
    css学习之样式层级和权重
    mysql中engine=innodb和engine=myisam的区别(转)
    mysql 创建表格 AUTO_INCREMENT
    mysql数据表的字段操作
    navicate使用小技巧
  • 原文地址:https://www.cnblogs.com/lifei01/p/13381386.html
Copyright © 2011-2022 走看看