一、序列化:模型转换为JSON流程(只传 instance)
- 序列化的类应在该应用中单独创建一个
serializers.py
来编写- 定义序列化器类(模型名/类视图名+
Serializer
),并继承自Serializer
- 定义序列化器中的字段应参照模型(
models.py
),序列化器中的字段名需要与模型一致,字段可以比模型字段多或少 - 对于有外键字段(
OneToOneField
、ManyToManyField
、ForeignKey
),在有外键字段里面关联序列化时用外键名,在没有外键字段里面关联序列化多时用模型名小写_set - 在没有外键的一方关联序列化时需要指定关联字段
many=true
- 将序列化模型对象或查询集(多个模型对象),传给序列化器类的
instance
参数,如果传的查询集需要在指定many=true
- 获取序列化后的数据,通过
序列化器对象.data
属性即可获取 - 序列化仅仅是模型转字典
- 定义序列化器类(模型名/类视图名+
1.1 创建数据模型
创建数据模型后,在后台添加点测试数据
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=32)
created_time = models.DateField(auto_now_add=True)
publish = models.ForeignKey('Publish',on_delete=models.CASCADE)
class Meta: # 模型元选项
# db_table = 'tb_column' # 在数据库中的表名,否则Django自动生成为app名字_类名
# ordering = ['index']
verbose_name = '书名'
verbose_name_plural = verbose_name
def __str__(self):
return self.title
class Author(models.Model):
name = models.CharField(max_length=20)
book = models.ManyToManyField(Book)
class Meta: # 模型元选项
# db_table = 'tb_column' # 在数据库中的表名,否则Django自动生成为app名字_类名
# ordering = ['index']
verbose_name = '作者'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class Publish(models.Model):
publish = models.CharField(max_length=32)
class Meta: # 模型元选项
# db_table = 'tb_column' # 在数据库中的表名,否则Django自动生成为app名字_类名
# ordering = ['index']
verbose_name = '出版社'
verbose_name_plural = verbose_name
def __str__(self):
return self.publish
class AuthorDetail(models.Model):
address = models.CharField(max_length=32)
author = models.OneToOneField(Author,on_delete=models.CASCADE)
class Meta: # 模型元选项
# db_table = 'tb_column' # 在数据库中的表名,否则Django自动生成为app名字_类名
# ordering = ['index']
verbose_name = '详情'
verbose_name_plural = verbose_name
def __str__(self):
return self.address
1.2 新建serializers.py,简单序列化
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
title = serializers.CharField(max_length=32)
created_time = serializers.DateField()
- 序列化模型对象
python manage.py shell
In [3]: from book.serializers import BookSerializer
In [4]: from book.models import Book
In [5]: book = Book.objects.get(pk=1)
In [6]: ser = BookSerializer(book)
In [7]: ser.data
Out[7]: {'title': 'Python', 'created_time': '2021-10-08'}
- 序列化查询集(多个模型对象,
queryset
对象)
In [1]: from book.serializers import BookSerializer
In [2]: from book.models import Book
In [3]: books = Book.objects.all()
In [4]: sers = BookSerializer(books,many=True)
In [5]: sers.data
Out[5]: [OrderedDict([('title', 'Python'), ('created_time', '2021-10-08')]), OrderedDict([('title', 'Vue'), ('created_time', '2021-10-08')])]
单获取的是多个对象需要指定
many=True
1.3 添加额外字段
- @property定义模型只读属性
# models.py 为 Book模型添加@property方法
class Book(models.Model):
title = models.CharField(max_length=32)
created_time = models.DateField(auto_now_add=True)
publish = models.ForeignKey('Publish',on_delete=models.CASCADE)
class Meta: # 模型元选项
# db_table = 'tb_column' # 在数据库中的表名,否则Django自动生成为app名字_类名
# ordering = ['index']
verbose_name = '书名'
verbose_name_plural = verbose_name
def __str__(self):
return self.title
# @property是python的一种装饰器,是用来修饰方法的。
# 我们可以使用@property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性
# ,可以与所定义的属性配合使用,这样可以防止属性被修改。
@property
def ext(self):
return f'出版社:name={self.publish.publish}'
# serializers.py 序列化额外字段
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
title = serializers.CharField(max_length=32)
created_time = serializers.DateField()
# 通过定义模型只读属性获取,这里应与@propert装饰器作用下的函数名名称一致
ext = serializers.CharField(max_length=20)
In [1]: from book.serializers import BookSerializer
In [2]: from book.models import Book
In [3]: books = Book.objects.all()
In [4]: sers = BookSerializer(books,many=True)
In [5]: sers.data
Out[5]: [OrderedDict([('title', 'Python'), ('created_time', '2021-10-08'), ('ext', '出版社:name=CQJTU')]), OrderedDict([('title', 'Vue'), ('created_tim
e', '2021-10-08'), ('ext', '出版社:name=CQJTU')])]
book = Book.objects.get(pk=2)
In [7]: ser = BookSerializer(book)
In [8]: ser.data
Out[8]: {'title': 'Vue', 'created_time': '2021-10-08', 'ext': '出版社:name=CQJTU'}
- 序列化器中自定义
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=32)
created_time = models.DateField(auto_now_add=True)
publish = models.ForeignKey('Publish',on_delete=models.CASCADE)
class Meta: # 模型元选项
# db_table = 'tb_column' # 在数据库中的表名,否则Django自动生成为app名字_类名
# ordering = ['index']
verbose_name = '书名'
verbose_name_plural = verbose_name
def __str__(self):
return self.title
# @property是python的一种装饰器,是用来修饰方法的。
# 我们可以使用@property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性
# ,可以与所定义的属性配合使用,这样可以防止属性被修改。
@property
def ext(self):
return f'出版社:name={self.publish.publish}'
@property
def eee(self):
return f'作者:{[i.name for i in self.author_set.all()]}'
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
title = serializers.CharField(max_length=32)
created_time = serializers.DateField()
# 通过定义模型只读属性获取
ext = serializers.CharField(max_length=20) # 名称对应相同则不需要指定source
author = serializers.CharField(max_length=10,source="eee") # 不同时则需要指定source
# SerializerMethodField 在此序列化类中get_xxx来重写
ext_info = serializers.SerializerMethodField(label="额外字段")
def get_ext_info(self,obj):
return f'出版社:{obj.publish}'
In [1]: from book.serializers import BookSerializer
In [2]: from book.models import Book
In [3]: books = Book.objects.all()
In [4]: sers = BookSerializer(books,many=True)
In [5]: sers.data
Out[5]: [OrderedDict([('title', 'Python'), ('created_time', '2021-10-08'), ('ext', '出版社:name=CQJTU'), ('author', "作者:['AAA', 'asd']"), ('ext_info
', '出版社:CQJTU')]), OrderedDict([('title', 'Vue'), ('created_time', '2021-10-08'), ('ext', '出版社:name=CQJTU'), ('author', "作者:['ADSD']"), ('ext
_info', '出版社:CQJTU')])]
额外字段的新增方法主要有两种,一种是通过定义数据模型@property装饰器的方法来指定仅可读方法,这种方法中若定义时与额外字段相同则不需要指定
source
,反之;还有一种就是在序列化模型类中定义方法来修改,通过定义以get_额外字段名
的函数名方法来进行改下。
1.4 关联对象序列化
模型关系分为一对一、多对一和多对多三种,当我们想从一个模型中获取相关联的模型的数据时,也可大致分为三种方式,获取关联模型的主键(
外键名_id
、PrimaryKeyRelatedField
)、获取模型对应的__str__
方法返回的值(StringRelatedField
,用的较少)、还有就是获取关联模型的所有字段(将想要获取关联模型进行序列化后,然后在另外一个序列化类中利用显示出来即可),最后一种就是前面的额外字段的方法,但是有一定的局限性,有些情况不能很好的显示出来,但也可以实现。
1.4.1 获取对应外键字段主键
- ① 通过
外键名_id
方式获取对应的外键 id
# serializers.py
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
title = serializers.CharField(max_length=32)
created_time = serializers.DateField()
# 在 Django ORM 中,我们知道数据库中保存时为外键_id
publish_id = serializers.IntegerField()
- ②
PrimaryKeyRelatedField
,使用时需要添加read_only
或者queryset
# serializers.py
class BookSerializer(serializers.Serializer):
title = serializers.CharField(max_length=32)
created_time = serializers.DateField()
# 使用 PrimaryKeyRelatedField 应注意与模型里面的字段保持一致,
# 并且通常需要设置 `get_queryset`, or set read_only=`True`,否则报错
# publish = serializers.PrimaryKeyRelatedField(queryset=Publish.objects.all())
publish = serializers.PrimaryKeyRelatedField(read_only=True)
In [1]: from book.serializers import BookSerializer
In [2]: from book.models import Book
In [3]: books = Book.objects.all()
In [4]: sers = BookSerializer(books,many=True)
In [5]: sers.data
Out[5]: [OrderedDict([('title', 'Python'), ('created_time', '2021-10-08'), ('publish', 1)]), OrderedDict([('title', 'Vue'), ('created_time', '2021-10-08
'), ('publish', 1)])]
1.4.2 获取外键关联的模型方法名称__str__
方法
# serializers.py
... 同上
# 获取出版社__str__方法返回的信息
publish = serializers.StringRelatedField()
In [1]: from book.serializers import BookSerializer
In [2]: from book.models import Book
In [3]: books = Book.objects.all()
In [4]: sers = BookSerializer(books,many=True)
In [5]: sers.data
Out[5]: [OrderedDict([('title', 'Python'), ('created_time', '2021-10-08'), ('publish', 'CQJTU')]), OrderedDict([('title', 'Vue'), ('created_time', '2021
-10-08'), ('publish', 'CQJTU')])]
1.4.3 获取外键关联模型的所有字段信息
step1:
定义被获取关联模型的序列化字段信息step2:
在需要获取关联模型的序列劣化字段你信息,并用外键名来实例化step1
序列化的模型- 注意:必须是这个顺序,需要先有才能实例化
class PublishSerializer(serializers.Serializer):
publish = serializers.CharField(max_length=32)
class BookSerializer(serializers.Serializer):
title = serializers.CharField(max_length=32)
created_time = serializers.DateField()
# 使用 PrimaryKeyRelatedField 应注意与模型里面的字段保持一致,
# 并且通常需要设置 `get_queryset`, or set read_only=`True`,否则报错
# publish = serializers.PrimaryKeyRelatedField(queryset=Publish.objects.all())
# publish = serializers.PrimaryKeyRelatedField(read_only=True)
# 获取出版社__str__方法返回的信息
publish = PublishSerializer()
1.4.4 其它说明
- 对于正向查询:即有外键查无外键(三种关系)
Django ORM
模型中学到,外键在数据库中保存的名自动加了_id
,因此正向查询时就可以通过此来获取外键的主键了。通常情况下使用外键关联时,无须反序列化可以都设置为read_only=True
。在多对多时需要指定many=True
,同查询集。
- 对于反向查询:即无外键查询有外键的模型(三种关系同)
反向查询可以通过
模型名小写_set
来获取相对应得查询集,这里除了一对一关系不需要指定many=True
,其余都需要指定many=True
。一对一不需要加_set
哦,小写表名就可以啦。
二、常用序列化字段及参数
官方字段及参数详情地址
选项参数
参数名称 | 作用 |
---|---|
max_length | 最大长度 32 |
min_lenght | 最小长度 2 |
allow_blank | 是否允许为空 False |
trim_whitespace | 是否截断空白字符 True |
max_value | 最小值 0 |
min_value | 最大值 100 |
通用参数
参数名称 | 说明 |
---|---|
read_only | 表明该字段仅用于序列化输出,默认False |
write_only | 表明该字段仅用于反序列化输入,默认False |
required | 表明该字段是否必须传,在反序列化时必须输入,默认True。 |
default | 反序列化时使用的默认值 |
allow_null | 表明该字段是否允许传入None,默认False |
validators | 该字段使用的验证器 |
error_messages | 包含错误编号与错误信息的字典 |
label | 用于HTML展示API页面时,显示的字段名称 |
help_text | 用于HTML展示API页面时,显示的字段帮助提示信息 |
三、反序列化:JSON转换为模型流程(只要传了data)
反序列化:从前端获取数据、序列化器的
data
、调用序列化器的is_valid()
方法来叫校验、调用序列化器的save()
方法、最后再进行序列化
- 获取前端传入的
json
字典数据 - 创建序列化器,给序列化器的
data
参数传参(需要以关键字参数进行传参) - 调用序列化器的
is_valid(raise_excption=True)
进行校验,若校验出错自动抛出错误信息 - 调用序列化器的
save()
方法,调用save
时会判断当初始序列化器中是否传入了instance
- 若 传入了
instance
也传入了data
,那么调用save方法实际调用的就是序列化器中的update
方法,若只有data
则是调用序列化器中的create
方法 - 反序列化最后会自动完成序列化,也是通过
ser.data
获取
3.1 反序列化校验方法
3.1.1 字段选项参数
在序列化中通过设置参数,如长度等
title = serializers.CharField(max_length=32)
3.1.2 自定义方法校验
自定义方法有三种形式,可以组合起来使用:
- validate(联合校验,全部字段自定义校验,定义的方法函数名,位于序列化类中)
- validate_字段名(局部校验,校验该字段名的数据,定义的方法函数名,位于序列化类中,一般就用联合校验就顺便把局部校验做了)
- validators(字段参数,定义的方法函数名可以任意,位于序列化类外,使用方法通过在字段中validators=[method1,method2])
from rest_framework import serializers
def about_publish(value):
if 'django' not in value.lower():
raise serializers.ValidationError("未含有Django!")
return value
class PublishSerializer(serializers.Serializer):
publish = serializers.CharField(max_length=32,validators=[about_publish])
datetime = serializers.SerializerMethodField(read_only=True)
def get_datetime(self,obj):
# 额外参数
import datetime
return f'{datetime.datetime.now()}'
def validate(self, attrs):
if '出版社' not in attrs['publish'].lower():
raise serializers.ValidationError("未含有出版社!")
return attrs
3.2 保存数据
验证数据成功后,用序列化器来完成数据反序列化,把数据转成模型类对象,通过实现
create()
和update()
两个方法来实现。如果创建序列化器对象的时候,没有传递instance
实例,则调用save()
方法的时候,create()
被调用;如果传递了instance
实例,则调用save()
方法的时候,update()
被调用。
from rest_framework import serializers
def about_publish(value):
if 'django' not in value.lower():
raise serializers.ValidationError("未含有Django!")
return value
class PublishSerializer(serializers.Serializer):
publish = serializers.CharField(max_length=32,validators=[about_publish])
datetime = serializers.SerializerMethodField(read_only=True)
def get_datetime(self,obj):
# 额外参数
import datetime
return f'{datetime.datetime.now()}'
def validate(self, attrs):
if '出版社' not in attrs['publish'].lower():
raise serializers.ValidationError("未含有出版社!")
return attrs
def create(self, validated_data):
# 新增数据
publish = Publish.objects.create(**validated_data)
return publish
def update(self, instance, validated_data):
# 更新数据,这里的instance就是传入的install = 模型.objects.get(id=1) (obj模型对象)
instance.publish = validated_data["publish"]
instance.save()
return instance
3.2.1 新增数据 调用 create
In [11]: from book.serializers import PublishSerializer
In [12]: from book.models import Publish
In [13]: data = {
...: "publish":"Django出版社DRF"
...: }
In [14]: ser = PublishSerializer(data=data)
In [15]: ser.is_valid(raise_exception=True)
Out[15]: True
In [16]: ser.save()
Out[16]: <Publish: Django出版社DRF>
In [17]: ser.data
Out[17]: {'publish': 'Django出版社DRF', 'datetime': '2021-10-09 10:03:09.150345'}
In [18]: [i.publish for i in Publish.objects.all())
File "<ipython-input-18-24069f24a3bb>", line 1
[i.publish for i in Publish.objects.all())
^
SyntaxError: closing parenthesis ')' does not match opening parenthesis '['
# 可以看到新增成功
In [19]: [i.publish for i in Publish.objects.all()]
Out[19]: ['CQJTU', 'Djangosa出版社', 'Djangosa出版社', 'Django出版社DRF']
3.2.2 更新数据 调用 update
In [1]: from book.models import Publish
# 修改前
In [2]: [i.publish for i in Publish.objects.all()]
Out[2]: ['CQJTU', 'Djangosa出版社', 'Djangosa出版社', 'Django出版社DRF']
# 指定修改哪一个
In [3]: pub = Publish.objects.get(pk=1)
In [4]: from book.serializers import PublishSerializer
In [5]: ser = PublishSerializer(instance=pub,data={'publish':'CQJTU出版社Django'})
In [6]: ser.is_valid()
Out[6]: True
In [7]: ser.save()
Out[7]: <Publish: CQJTU出版社Django>
In [8]: ser.data
Out[8]: {'publish': 'CQJTU出版社Django', 'datetime': '2021-10-09 10:10:28.303865'}
# 修改后
In [9]: [i.publish for i in Publish.objects.all()]
Out[9]: ['CQJTU出版社Django', 'Djangosa出版社', 'Djangosa出版社', 'Django出版社DRF']
3.3 附加说明
- 1) 在对序列化器进行save()保存时,可以额外传递数据,这些数据可以在create()和update()中的validated_data参数获取到
# request.user 是django中记录当前登录用户的模型对象
serializer.save(owner=request.user)
- 2)默认序列化器必须传递所有required的字段,否则会抛出验证异常。但是我们可以使用partial参数来允许部分字段更新详情
# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)
四、模型序列化器类使用说明
ModelSerializer
继承自Serializer
- 它可以根据模型来自动生成序列化器中的字段
- 它里面实现了
create
和update
方法 - 但是我们也可以如通用
Serializer
来重写一些自定义方法来校验
class BookModelSerializer(serializers.ModelSerializer):
ext = serializers.CharField()
class Meta:
''' 指定需要序列化模型'''
model = Book
''' 指定模型中需要序列化的字段
新增字段需要加上在后面加上
'''
# 模型中所有字段
# fields = '__all__'
# 模型中部分字段
# fields = ['title','created_time','ext']
''' 排除模型中需要序列化的字段
新增字段不需要加上
'''
# 除了 title 不需要其他全都需要(包括新增字段)
exclude = ['title',]
'''修改字段里面的参数'''
# 当生成自动的参数不满足需求时,当然也可以自定方法来进行校验,方法同 Serializer
# 此处不可修改 read_only = True
extra_kwargs = {
'title':{
'write_only':True,
'max_length':32,
},
'created_time':{
'format':"%Y-%m-%d %H:%M:%S"
}
}
'''指定哪些字段只做序列化'''
read_only_fields = ["created_time"]
# 额外参数
def get_ext(self,obj):
return f'其他参数:{obj.title}'
# 自定义方法校验
def validate(self, attrs):
if 'django' not in attrs["title"].lower():
raise serializers.ValidationError("书名没有包含有Django")
return attrs
Serializer
不仅仅是可用于校验数据存储,还可以用于存于无数据模型中如redis
等的校验,而ModelSerializer
仅适用于有数据模型时使用。