DRF的序列化与反序列化

序列化:

序列化:将模型,查询集转换成需要的格式的数据,比如json。一般为输出
model.py的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from django.db import models

class BookInfo(models.Model):
btitle = models.CharField(max_length=20, verbose_name='名称')
bpub_date = models.CharField(max_length=20, verbose_name='发布日期')
bread = models.IntegerField(default=0)
bcomment = models.IntegerField(default=0)
is_delete = models.BooleanField(default=False)
image = models.ImageField()

class Meta:
db_table = 'tb_books'
verbose_name = 'books'
verbose_name_plural = verbose_name

def __str__(self):
return self.btitle

class HeroInfo(models.Model):
#略写
id = models.CharField(read_only=True)
hname = models.CharField(max_length=20)
hgender = models.ChoiceField(choice = GENDER)
hcomment = models.CharField()
hbook = models.PrimaryKeyField(lable = 'shuji')#外键

serializers.py的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from rest_framework import serializers
from .se import BookInfo
class BookInfoSerializer(serializers.Serializer):
#定义序列化器,
#序列化器中的字段可以比模型中的字段多,也可以少,
#但是,模型中已经存在的字段,在写到序列化器中的时候,名字一定要是一样的
id = serializers.IntegerField(label = 'ID',read_only = True) #read_only属性指只做序列化,write_only代表只做反序列化,
#都不写的话默认是两者都可。
btitle = serializers.CharField(max_length=20, verbose_name='名称',required = True)
bpub_date = serializers.CharField(max_length=20, verbose_name='发布日期')
bread = serializers.IntegerField(default=0)
bcomment = serializers.IntegerField(default=0)
is_delete = serializers.BooleanField(default=False)
image = serializers.ImageField()

class HeroInfoSerializer(serializer.Serializer):
id = serializer.CharField(read_only=True)
hname = serializer.CharField(max_length20)
hgender = serializer.ChoiceField(choice = GENDER,required=False)
hcomment = serializer.CharField()
hbook = serializer.PrimaryKeyRelatedField(lable = 'shuji', read_only = True)#外键,只能序列化
#hbook = serializer.PrimaryKeyRelatedField(lable = 'shuji', queryset = BookInfo.objects.all())
#这里的queryset是只能反序列化使用,
#且,如果传过来的要反序列化的数据不是BookInfo的模型数据,就会报错

使用序列化器序列化

1
2
3
4
5
6
from serializers import BookInfoSerializer
book = BookInfo.objects.get(id = 1) #先取到要传出去的模型对象
s = BookInfoSerializer(instance = book)
#BookInfoSerializer(instance,data, many)给instance传值是用序列化器序列化,
#给data转值就是用序列化器反序列化,many参数,是,当instance是一个查询集的时候,就要many = True
s.data#就是获取序列化后的数据,一般为原生python数据类型 s.instance返回book,即没序列化的原始数据

关联序列化:

所谓关联序列化,就是当模型之间存在外键关联的时候,可以通过序列化这个表的时候,直接将关联的表也序列化掉。

1
2
3
hero = HeroInfo.objects.get(id = 1)
serializer = HeroInfoSerializer(instance = hero)
serializer.data
  1. 这里就会发现,HeroInfoSerializer中的hbook = serializer.PrimaryKeyRelatedField(lable = 'shuji', read_only = True)序列化的是id。

  2. 如果写为hbook = serializer.StringRelatedField(lable = 'shuji', read_only = True)是将__str__函数的返回值序列化出来。

  3. 如果写为hbook = BookInfoSerializer()会将关联的模型的所有字段序列化出来。

上面总共三种情况,都是在“一对多”中的“多”关联序列化的“一”,那如何在“一”中关联序列化“多”呢?

BookInfoSerializer中增加一个字段heroinfo_set = serializers.PrimaryKeyRelatedField(mang = True)这个关联序列化的是id字段,当然也可以和上面一样,有__str__函数等其他的序列化,比如heroinfo = HeroInfoSerialzier(many = True),当然,HeroInfoSerializer要在这个类前面。

注意,在多中关联序列化一,或者一中关联序列化多,这两者,只能二选一。

总结:

image-20210316123844737

反序列化:

反序列化:将前端数据转为模型数据。

首先先从前端拿到传来的数据,然后将其传给序列化器的data参数,然后调用序列化器的.is_valid()

方法进行校验,最后调用“序列化器.save()方法”。save方法,会根据传参情况,选择性的调用create方法或者update方法。create/update方法里面会再使用django的object.create方法保存至数据库。

1
2
3
4
5
6
7
8
9
10
11
12
13
#反序列化:
dataa = { #准备一个前端数据
'btitile': '三国',
'bpub_date': '1991-11-11'
}
serializer = BookInfoSerializer(data = dataa)#传给参数data,就是反序列化
serializer.is_valid()#调用序列化器的校验方法 是True还是False
# serializer.error#获取校验的错误信息
#serializer.is_valid(raise_exception = True)相当于is_validu与error一起使用了
serializer.validated_data#获取反序列化校验后的数据
#serializer.data是反序列化完成后,完成序列化的数据,因为反序列的过程中也有反序列化完成后,又序列化一下
#如果校验成功
serializer.save()#会执行序列化器中的create或update方法

如果想要增加自己的数据校验方法,可以直接在序列化器中增加:

增加额外的校验

1.如果想要对一个字段增加校验,可以:(以在BookInfoSerializer中增加校验为例)

1
2
3
4
5
def validate_btitle(self, value):
#value是当前要进行校验的单个字段的值
if 'hhh' not in value.lower():
raise serializers.ValidationError("hhh不在btitle中")
return value

此方法的命名规则是validate_字段, 此方法要return value,如果不返回,后面还要的serializer.validated_data就获取不到了。

2.如果想要对多个字段增加校验,可以:

1
2
3
4
5
6
7
def validate(self,attrs):
#联合校验,对多个字段校验,attrs里面是前端传过来的所有数据
bcomment = attrs['bcomment']
bread = attrs['bread']
if bcomment < bread:
raise serializers.ValidationError('评论小于阅读数')
return attrs

3.使用validators

比如我在BookInfoSerializer中的一个字段中添加

1
2
3
4
5

```python
def about_hhh:
if 'hhh' not in value.lower():
raise serializers.ValidationError("hhh不在btitle中")

所以,应用场景是,一个校验逻辑,多个字段都要以此去校验的时候,用这种写法。

serializer.save()

当我们校验成功之后,调用这个save方法会执行序列化器中的create或update方法。

image-20210316155725799

根据save函数的源码可知,当这样调用序列化器的时候BookInfoSerializer(instance = book,data = dataa )的时候,save调用的是update方法,如果只有data传参,instance没有接收到参数,那么save函数就调用create方法。

再看一下父类Serializer中的update和create方法没有实际的逻辑,所以,我们要save数据的时候,要在BookInfoSerializer中重构update方法和create方法。

1
2
3
4
5
6
7
8
9
10
11
def create(self,validated_data):
#validated_data是反序列化校验后的字典数据
book = BookInfo.objects.create(**validated_data)#本质还是调用orm中的create方法
return book #这里的返回值会赋给save函数中的self.instance, 所以book = serializer.save()这样可以接收到这个值

def update(self,instance, validated_data):
#instance是要修改的模型对象,这个参数的值创建序列化器时通过传参过来的。validated_data是反序列化校验后的字典数据,也通过传参过来。
instance.btitle = validated_data.get('btitle')#validated_data是字典,可以用[]也可以用.get
instance.bpub_date = validated_data.get('bpub_date')
instance.save()
return instance

总结:

image-20210316162109955

Serializer的构造方法是serializer(instance= None, data = empty, **kwarg)

所以Book序列化器的构造函数为这个BookInfoSerialzier(instance , data, context)第三个参数可以传字典。

ModelSerializer

ModelSerializer继承至Serializer

他比父类多的功能有:

1.他可以自动根据模型生成序列化器中的字段。

2.这个类帮我们实现了create和update方法。当然这些方法中的逻辑不一定是我们所需要的。

1
2
3
4
5
6
7
8
9
10
11
12
class BookInfoSerializer(serializers.ModelSerializer):
#pass = serializer.CharField()这样可以添加模型之外的字段,当然要配合Meta
class Meta:
model = BookInfo
fields = '__all__'
#fields = ['id','btitle','bpub_date'],这样可以指定映射特定的模型字段到序列化器
#exclude = ['image'],这样可以除了这个字段外的字段都映射到序列化器
extra_kwargs = { #这样可以对一字段的参数修改
'bread':{'min_value' :0,'require':True}
'bcomment' :{'write_only':True} #指定可以反序列化
}
read_only_fields = ['id', 'bread']#指定哪些字段只能序列化,注意没有write_only_fields

这样就直接按照BookInfo的模型的所有字段,生成了此序列化器中的字段。

序列化与反序列化如下:

1
2
3
4
5
6
7
8
ser = BookInfoSerializer(book)
ser.data

ser = BookInfoSerializer(instance = book, data = data)
ser.is_valid(raise_exception = True)
ser.save()
#ser.validated_data
#...

BaseSerializer

DRF的serializer和ModelSerialzier 都继承至BaseSerializer ,所以,我们可以看看序列化器都有哪些基本的属性和方法。

该类实现与Serializer类相同的基本API:

  • .data - 返回传出的原始数据。(是python的原生数据,返回的数据应该是序列化的数据)
  • .is_valid() - 反序列化并验证传入的数据。(是一个方法,返回True/False)
  • .validated_data - 返回经过验证后的传入数据(是字典等python原生数据类型)。
  • .errors - 返回验证期间的错误。
  • .save() - 将验证的数据保留到对象实例中。(会调用create/update方法)

它还有可以覆写的四种方法,具体取决于你想要序列化类支持的功能:

  • .to_representation() - 重写此方法来改变读取操作的序列化结果。
  • .to_internal_value() - 重写此方法来改变写入操作的序列化结果。
  • .create().update() - 重写其中一个或两个来改变保存实例时的动作。

因为此类提供与Serializer类相同的接口,所以你可以将它与现有的基于类的通用视图一起使用,就像使用常规SerializerModelSerializer一样。

这样做时你需要注意到的唯一区别是BaseSerializer类并不会在可浏览的API页面中生成HTML表单。