Django框架之序列化和反序列化
在使用Django中的DRF框架时,它提供了一个非常重要的功能就是序列化器,比如我们需要将前端接收到处理后的json数据转化为模型,或者向前端返回数据时需要将模型对象转化为json数据,那么这里将模型转化为json的过程称之为序列化,将json数据转化为模型称之为反序列化
定义序列化器
为了得到模型里的字段,序列化器中字段应与模型类中的字段名一致
自定义字段
定义方法
Serializer继承于rest_framework.serializers.Serializer模块
比如我们这里定义一个图书类的序列化器和英雄类的序列化器
图书类序列化器
from rest_framework import serializers
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名称', max_length=20)
bpub_date = serializers.DateField(label='发布日期', required=False)
bread = serializers.IntegerField(label='阅读量', required=False)
bcomment = serializers.IntegerField(label='评论量', required=False)
image = serializers.ImageField(label='图片', required=False)
1V多关联对象字段定义
heroinfo_set = serializers.PrimarKeyRelatedField(label='英雄', read_only=true,many=True)
英雄类序列化器
class HeroInfoSerializers(serializers.Serializer):
GENDER_CHOICES = (
(0, 'male'),
(1, 'female')
)
id = serializers.IntegerField(label="ID", read_only=True)
hname = serializers.CharField(label='名字', max_length=20)
hgender = serializers.ChoiceField(
label='性别', choices=GENDER_CHOICES, required=False)
hcomment = serializers.CharField(
label='描述信息', max_length=200, required=False, allow_null=True)
# 多V1关联对象字段定义
# hbook = serializers.PrimaryKeyRelatedField(label='图书',read_only=True)
# hbook = serializers.HyperlinkedRelatedField(label='图书', read_only=True, view_name='books-detail')
hbook = BookRelateField(read_only=True)
- 注:
括号中的read_only参数表示只有序列化时使用,required表示是否必须传入,默认为True
字段类型与选项参数
字段 | 字段构造方式 |
---|---|
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页面时,显示的字段帮助提示信息 |
利用模型类快速定义
1.定义方法:
Serializer继承于rest_framework.serializers.ModelSerializer模块;
此时指明每个字段的序列化类型和选项参数,本身是为后续提供一种验证行为;
2.提供的功能:
-
基于模块自动生成一系列字段
-
基于模型类自动为Serializer生成validators
-
包含默认的create()和update()
同样这里以图书类和英雄类做案例
图书类定义:
class BookInfoSerializer(serializers.ModelSerializer):
# 显示指定字段
heroinfo_set = serializers.PrimaryKeyRelatedField(label='英雄',read_only=True,many=True)
"""图书数据序列化器"""
class Meta:
model = BookInfo
fields = '__all__' #表示展示所有字段
fields = ('id', 'btitle', 'bpub_date') #表示只显示对应字段
exclude = ('image',) # 除image外的所有字段,元组类型
depth = 1 # 嵌套层级
read_only_fields = ('id', 'bread', 'bcomment') #仅用于序列化输出
英雄类定义
class HeroInfoModelSerializers(serializers.ModelSerializer):
class Meta:
model = HeroInfo
fields = ('id', 'hname', 'hgender', 'hbook')
depth = 1
extra_kwargs = {
'bread': {'min_value': 0, 'required': True}},
'bcomment': {'max_value': 0, 'required': True}},
}
-
注:
-
model 指明参照哪个模型类
-
fields指明为模型类的哪些字段生成
-
exclude 指明排除掉哪些字段
-
depth 指明嵌套层级数量
-
在Meta上方可以指明显示指定字段
-
Extra_kwargs 添加或修改原有字段,优先级高
-
二、序列化
(一)创建Serializer对象
Serializer对象的构造方式
序列化器名(instance=None,data=empty,**kwary)
-
注:
-
instance表示模型类对象
-
data用于反序列化传入的数据
-
**wkargs可以传入带名字的参数,其中包括context,该方法可以通过Serializer对象的context属性获取
-
(二)基本查询
from booktest.models import BookInfo
from booktest.serializers import BookInfoSerializer
#获取单个数据
book = BookInfo.objects.get(id=2)
serializer = BookInfoSerializer(book) #序列化器初始化
serializer.data #通过data方法获取数据
#获取多个数据需要用many参数标注
book_qs = BookInfo.objects.all()
serializer = BookInfoSerializer(book_qs, many=True)
serializer.data
关联对象查询
1.一对多关联查询
1:n 图书:英雄
关联模型类名小写_set 作为字段名,同时需要通过many参数标注多个数据
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名称', max_length=20)
bpub_date = serializers.DateField(label='发布日期', required=False)
bread = serializers.IntegerField(label='阅读量', required=False)
bcomment = serializers.IntegerField(label='评论量', required=False)
image = serializers.ImageField(label='图片', required=False)
heroinfo_set = serializers.PrimaryKeyRelatedField(read_only=True, many=True) # 新增
2.多对一关联查询
n:1 英雄:图书
class HeroInfoSerializer(serializers.Serializer):
"""英雄数据序列化器"""
GENDER_CHOICES = (
(0, 'male'),
(1, 'female')
)
id = serializers.IntegerField(label='ID', read_only=True)
hname = serializers.CharField(label='名字', max_length=20)
hgender = serializers.ChoiceField(choices=GENDER_CHOICES, label='性别', required=False)
hcomment = serializers.CharField(label='描述信息', max_length=200, required=False, allow_null=True)
hbook = serializers.PrimaryKeyRelatedField(label='图书', read_only=True)
3.关联字段采用的方法:
-
PrimaryKeyRelatedField
-
StringRelatedField
-
HyperlinkedRelatedField
-
HyperlinkedRelatedField
-
使用关联对象的序列化器
-
重写to_representation方法
案例:
hbook = serializers.PrimaryKeyRelatedField(label='图书', read_only=True)
或
hbook = serializers.PrimaryKeyRelatedField(label='图书', queryset=BookInfo.objects.all())
hbook = serializers.StringRelatedField(label='图书')
hbook = serializers.HyperlinkedRelatedField(label='图书', read_only=True, view_name='books-detail')
hbook = serializers.SlugRelatedField(label='图书', read_only=True, slug_field='bpub_date')
hbook = BookInfoSerializer()
class BookRelateField(serializers.RelatedField):
"""自定义用于处理图书的字段"""
def to_representation(self, value):
return 'Book: %d %s' % (value.id, value.btitle)
三、反序列化
反序列化分为两步:
-
数据校验
-
数据保存
验证数据
使用序列化器进行反序列化之前需要进行验证,验证通过后才可以得到数据从而保存为模型类对象;
在获取序列化数据前,先通过is_valid方法进行验证,它会返回一个布尔值True或者False;
验证失败返回False,此时可以通过errors方法回去错误原因;
验证成功后返回True,此时可以通过validated_data方法获取数据;
校验方法一:
按照定义序列化器是规定的类型进行验证,比如DateField类型必须是时间类型
class BookInfo(models.Model):
btitle = models.CharField(max_length=20, verbose_name='名称')
bpub_date = models.DateField(verbose_name='发布日期', null=True)
……
#----------以上是序列化器中的内容-----------
from booktest.serializers import BookInfoSerializer
data = {'bpub_date': 123}
# 创建序列化器对象
# 序列化第二个参数data用于反序列化,传入字典类型
# 需要用is_valid进行验证,参数raise_exception设置为True 来向前端抛出异常
# 其中属性errors可以查看报错原因,validated_data用于获取数据
serializer = BookInfoSerializer(data=data)
serializer.is_valid(raise_exception=True) # 返回False,
serializer.errors
# {'btitle': [ErrorDetail(string='This field is required.', code='required')], 'bpub_date': [ErrorDetail(string='Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].', code='invalid')]}
serializer.validated_data # {}
校验方法二:
字段选项
比如:
选项参数:
参数名称 | 作用 |
---|---|
max_length = 10 | 最大长度不超过10 |
min_lenght =1 | 最小长度不低于1 |
allow_blank | 是否允许为空 |
trim_whitespace | 是否截断空白字符 |
max_value | 最小值 |
min_value | 最大值 |
检验方法三:
对单独字段进行验证
语法格式:在序列化器中实现方法 def validate_ 字段名(self,value)
注:参数value表示传入的字段值
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
btitle = models.CharField(max_length=20, verbose_name='名称')
bpub_date = models.DateField(verbose_name='发布日期', null=True)
...
def validate_btitle(self, value):
if 'django' not in value.lower():
raise serializers.ValidationError("图书不是关于Django的")
return value
测试
from booktest.serializers import BookInfoSerializer
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # False
serializer.errors
# {'btitle': [ErrorDetail(string='图书不是关于Django的', code='invalid')]}
校验方法四:
对多个字段进行校验
语法格式:def validate(self,attrs)
注:参数attrs其实就是data字典
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
...
def validate(self, attrs):
bread = attrs['bread']
bcomment = attrs['bcomment']
if bread < bcomment:
raise serializers.ValidationError('阅读量小于评论量')
return attrs
测试:
from booktest.serializers import BookInfoSerializer
data = {'btitle': 'about django', 'bread': 10, 'bcomment': 20}
s = BookInfoSerializer(data=data)
s.is_valid() # False
s.errors
# {'non_field_errors': [ErrorDetail(string='阅读量小于评论量', code='invalid')]}
校验方法五
自定义校验方法
def about_django(value):
if 'django' not in value.lower():
raise serializers.ValidationError("图书不是关于Django的")
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名称', max_length=20, validators=[about_django])
……
测试
from booktest.serializers import BookInfoSerializer
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # False
serializer.errors
# {'btitle': [ErrorDetail(string='图书不是关于Django的', code='invalid')]}
(二) 保存数据
在调用serializer中的save方法时,我们需要手动创建create()和update()方法
在创建序列化对象时,可以通过partial=True来允许部分字段更新
创建新数据
当创建序列化对象时只传入data数据时即为创建新数据,自动调用序列化器中的create方法
在序列化器中定义create方法
注:参数**validated_data其实就是传入的data字典数据
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
...
def create(self, validated_data):
"""新建"""
return BookInfo.objects.create(**validated_data)
测试
from db.serializers import BookInfoSerializer
data = {'btitle': '封神演义'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # True
serializer.save() # <BookInfo: 封神演义>
更新数据
当创建序列化对象时传入实例instance对象和data数据时,即为更新数据
传入两个数据系统会自动调用update方法、
注:instance为要更新的实例对象,validated_data为更新数据
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
...
def create(self, validated_data):
"""新建"""
return BookInfo.objects.create(**validated_data)
def update(self, instance, validated_data):
"""更新,instance为要更新的对象实例"""
instance.btitle = validated_data.get('btitle', instance.btitle)
instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
instance.bread = validated_data.get('bread', instance.bread)
instance.bcomment = validated_data.get('bcomment', instance.bcomment)
instance.save() #注意需要保存数据
return instance