drf序列化与反序列化

序列化类常用字段类和字段参数

常用字段类

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=)

重点记住几个

CharField
BooleanField
IntergerField
DecimalField

# ListField:{name:'kevin',age:19,hobby:['篮球','足球']}
# DictField:{name:'kevin',age:19,wife:{'name':'刘亦菲','age':33}}

常用字段参数

给CharField字段类使用的参数

参数名称 作用
max_length 最大长度
min_length 最小长度
allow_black 是否允许为空
trim_whitespace 是否截断空白字符

给IntegerField字段类使用的参数

参数名称 作用
max_value 最小值
min_value 最大值

通用参数

放在哪个字段类上都是可以的

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

序列化类高级用法之source,修改序列化字段名字

source指定的可以是字段;也可以是方法,用于重命名;也可以做跨表查询。

source指定字段

在序列化book表时,name字段在前端显示的时候叫book_name

'在自定义序列化类中使用source字段参数,可以指定序列化表中的那个字段'
book_name=serializers.CharField(source='name')

source指定方法

class BookSerializer(serializers.Serializer):
    book_name=serializers.CharField(source='name')
    price=serializers.IntegerField()
    publish=serializers.CharField(source='publishnb')

class Book(models.Model):
    name=models.CharField(max_length=32)
    price=models.IntegerField()
    publish=models.CharField(max_length=32)
    def publishnb(self):
        return self.publish+'nb'

source的publishnb表示表模型的方法

source指定跨表查询

除此之外,source还可以用于跨表查询,直接用orm的基于对象的跨表查询方式

publish_name = serializers.CharField(max_length=8, min_length=3,source='publish.name')

序列化类高级用法之定制序列化字段的两种方式

在序列化类中写SerializerMethodField

表模型

class Book(models.Model):
    name=models.CharField(max_length=32)
    price=models.IntegerField()
    publish=models.ForeignKey(to='Publish',on_delete=models.CASCADE)
    
class Publish(models.Model):
    name=models.CharField(max_length=32)
    city=models.CharField(max_length=32)

序列化类

class BookSerializer(serializers.Serializer):
    book_name=serializers.CharField(source='name')
    price=serializers.IntegerField()
    publish=serializers.SerializerMethodField()
    # 在序列化类中写
    # obj表示当前序列化的对象:book
    def get_publish(self,obj):
       return {'name':obj.publish.name,'city':obj.publish.city}

在表模型中写方法

表模型

class Book(models.Model):
    name=models.CharField(max_length=32)
    price=models.IntegerField()
    publish=models.ForeignKey(to='Publish',on_delete=models.CASCADE)
    def publish_detail(self):
        return {'name':self.publish.name,'city':self.publish.city}
class Publish(models.Model):
    name=models.CharField(max_length=32)
    city=models.CharField(max_length=32)

序列化类

class BookSerializer(serializers.Serializer):
    book_name=serializers.CharField(source='name')
    price=serializers.IntegerField()
    # 要用DictField()序列化字典数据
    publish_detail=serializers.DictField()
image-20220927174320953

多表操作之使用在序列化类中自定义序列化字段

使用SerializerMethodField类自定义序列化字段,需要在方法中传入一个obj对象,就是序列化对象,基于此对象进行跨表操作。

'表模型'
class Book(models.Model):
    title=models.CharField(max_length=32)
    price=models.IntegerField()
    pub_date=models.DateField(auto_now_add=True,null=True)
    publish=models.ForeignKey("Publish",on_delete=models.CASCADE,null=True)
    authors=models.ManyToManyField("Author")

class Publish(models.Model):
    name=models.CharField(max_length=32)
    city=models.CharField(max_length=32,null=True)
    email=models.EmailField(null=True)

class Author(models.Model):
    name=models.CharField(max_length=32)
    age=models.IntegerField(null=True)
    city=models.CharField(max_length=32,null=True)
    
'自定义序列化类'
from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    title=serializers.CharField()
    price=serializers.IntegerField()
    pub_date=serializers.DateField()
    publish=serializers.SerializerMethodField()
    authors=serializers.SerializerMethodField()
    # obj当前序列化的对象book
    # 通过基于对象点的形式跨表查询
    def get_publish(self,obj):
        return {'name':obj.publish.name,'city':obj.publish.city,'email':obj.publish.email}

    # 多对多关系,需要返回列表套字典的形式
    def get_authors(self,obj):
        author_list=[]
        for author in obj.authors.all():
            author_list.append({'name':author.name,'age':author.age,'city':author.city})
        return author_list
image-20220927180722354

多表操作之在表模型中自定义序列化字段

在表模型中写方法,方法名为序列化类的字段名

'表模型'
class Book(models.Model):
    title=models.CharField(max_length=32)
    price=models.IntegerField()
    pub_date=models.DateField(auto_now_add=True,null=True)
    publish=models.ForeignKey("Publish",on_delete=models.CASCADE,null=True)
    authors=models.ManyToManyField("Author")

    def publish_detail(self):
        return {'name':self.publish.name,'city':self.publish.city,'email':self.publish.email}

    def author_detail(self):
        author_list = []
        for author in self.authors.all():
            author_list.append({'name': author.name, 'age': author.age, 'city': author.city})
        return author_list

class Publish(models.Model):
    name=models.CharField(max_length=32)
    city=models.CharField(max_length=32,null=True)
    email=models.EmailField(null=True)
class Author(models.Model):
    name=models.CharField(max_length=32)
    age=models.IntegerField(null=True)
    city=models.CharField(max_length=32,null=True)

'自定义序列化类'
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
    title=serializers.CharField()
    price=serializers.IntegerField()
    pub_date=serializers.DateField()
    # DictField将字典形式的publish_detail返回的数据进行序列化操作
    publish_detail=serializers.DictField()
    # ListField()将列表套字典形式的author_detail返回的数据进行序列化操作
    author_detail=serializers.ListField()

有关联关系表的反序列化的保存

class BookSerializer(serializers.Serializer):
    title=serializers.CharField()
    price=serializers.IntegerField()
    pub_date=serializers.DateField()
    
    # 序列化
    # DictField将字典形式的publish_detail返回的数据进行序列化操作
    publish_detail=serializers.DictField(read_only=True)
    # ListField()将列表套字典形式的author_detail返回的数据进行序列化操作
    author_detail=serializers.ListField(read_only=True)

    # 反序列化,需要重新写字段,和序列化字段不一样:前端返回回来的id以及列表
    publish=serializers.IntegerField(write_only=True)
    authors=serializers.ListField(write_only=True)
		
    # 一定要重写create
    def create(self, validated_data):

        book_obj=Book.objects.create(
            title=validated_data.get('title'),
            price=validated_data.get('price'),
            pub_date=validated_data.get('pub_date'),
            publish_id=validated_data.get('publish'),
        )
        book_obj.authors.add(*validated_data.get('authors'))
        return book_obj

使用继承Serializer的序列化类保存需要重写create方法

缺点

  1. 在序列化中每个字段要写,无论是序列化还是反序列化
  2. 如果新增或者修改,在序列化类中都需要重写create或update

解决这个缺陷

使用ModelSerializer来做

补充

在模型类中写逻辑代码,称之为ddd,领域驱动模型

反序列化之数据校验

反序列化的数据校验和forms组件很像,它等于是分为三层校验:

  • 字段自己的校验规则
  • 局部钩子
  • 全局钩子

字段自己的校验规则

如果继承的是Serializer,写法如下:

title=serializers.CharField(max_length=8,min_length=3,error_messages={
        'min_length':'书籍名最少为3个字符'
    })

如果继承的是ModelSerializer,写法如下:

from rest_framework.serializers import ModelSerializer
from .models import Book
class BookSerializer(ModelSerializer):
    class Meta:
        model=Book
        fields='__all__'
        extra_kwargs={
            'title':{
                'max_length':8,'min_length':3,'error_messages':{
                    'max_length':'最长8位',
                    'min_length':'最短3位'
                }
            }
        }

局部钩子

继承Serializer和继承ModelSerializer的写法一样

from rest_framework.exceptions import ValidationError
def validate_title(self,title):
  if title=='水浒传':
    raise ValidationError('书籍名不能为水浒传')
  else:
    return title

全局钩子

继承Serializer和继承ModelSerializer的写法一样

    def validate(self, attrs):  # attrs表示要校验的数据
        title=attrs.get('title')
        price=attrs.get('price')
        if price>80 or len(title)>6:
            raise ValidationError('价格不能大于80或者书籍名不能超过6位')
        else:
            return attrs    # 返回校验的数据

模型类序列化器(ModelSerializer)的使用

案例

注意点:

  • 自定义序列化字段应加read_only参数,这样反序列化的结果无此字段结果集
  • 自定义反序列化字段应加write_only参数,这样序列化的结果无此字段结果集

使用SerializerMethod自定义ModelSerializer字段

class BookSerializer(serializers.ModelSerializer):
  	# 不需要写字段了,字段从表模型映射出来
    class Meta:
      # 要序列化的表模型
        model=Book
      # 所有字段都要序列化
        fields=['title','price','pub_date','publish','authors','publish_detail','author_detail']
        extra_kwargs={
            'title':{
                'max_length':8,'min_length':3,'error_messages':{
                    'max_length':'最长8位',
                    'min_length':'最短3位'
                },

            },
            'publish': {
                'write_only': True
            },
            'authors':{
                'write_only':True
            }
        }
    publish_detail=serializers.SerializerMethodField(read_only=True)
    author_detail=serializers.SerializerMethodField(read_only=True)

    def get_publish_detail(self,obj):
        return {'name':obj.publish.name,'city':obj.publish.city,'email':obj.publish.email}

    def get_author_detail(self,obj):
        author_list=[]
        for author in obj.authors.all():
            author_list.append({'name':author.name,'age':author.age,'city':author.city})
        return author_list

以上总结

ModelSerializer的使用步骤详解

  1. 定义一个类继承ModelSerializer
  2. 在类内部写内部类class Meta
  3. 在内部类指定model:要序列化的表
  4. 在内部类中指定fields(要序列化的字段,写__all__表示所有,不包含方法,写一个个字段)
  5. 在内部类中指定extra_kwargs,给字段添加字段参数的
  6. 在序列化类中,可以重写某个字段,优先使用你重写的
title=serializers.SerializerMethodField(read_only=True)
def get_title(self,obj):
  return '爆款'+obj.title
  1. 以后不需要重写createupdate
ModelSerializer写好了,兼容性更好,任意表都可以直接存

反序列化数据校验源码分析

反序列化数据校验流程

  1. 先校验字段自己的规则(根据参数进行校验)
  2. 局部钩子校验
  3. 全局钩子校验

反序列化源码分析

注意:在查看源码的时候,我们应注意对象的方法查找顺序,对象点方法,必须先从对象本身逐层往上找

image-202209272112318711

is_vaild是我们查看反序列化源码的入口,它里面封装了三层校验的方法,必须执行此方法才会走三层校验的f方法:字段校验、局部钩子校验、全局钩子校验。

image-20220927211414219

self.run_validation,我们通过对象自身查找关系,可以查找到run_validation在ModelSerializer的父类Serializer中,它里面封装了三层校验的方法:

  • 字段校验、局部钩子校验方法:self.to_internal_value(data)
  • 全局钩子校验入口方法:seif.validate(value)
image-20220927211751792

局部钩子的方法

image-20220927211915392

全局钩子方法

断言assert

由于框架的源码中,大量使用断言,学习一下

断言的定义:断言,作用的判断,断定一个变量必须是xx,如果不是就报错

# 一般写法
name ='xxx'
if name=='xxx':
  raise Exception('name不能等于xxx')
...

#assert的断言用法
name='xf'
assert name!='xf','name必=须等于xf'
print('主')
posted @ 2022-09-28 00:22  荀飞  阅读(73)  评论(0编辑  收藏  举报