对数据进行转换 序列化(输出、read_only)和反序列化(输入、write_only)
进行数据的校验
判断用户发送的数据是否合法(is_valid(raise_exception=True))
DJango REST framework中的Serializer使用类来定义,须继承自rest_framework.serializers.Serializer
。
例如,已有了一个数据库模型类BooksInfo
class BooksInfo(models.Model):
title = models.CharField(max_length=20, verbose_name='名称')
pub_date = models.DateField(verbose_name='发布日期')
read = models.IntegerField(default=0, verbose_name="阅读量")
comment = models.IntegerField(default=0, verbose_name="评论量")
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
可以为这个模型类提供一个定义如下的序列化器:
class BooksInfoSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(label='名称', max_length=20)
pub_date = serializers.DateField(label='发布日期')
read = serializers.IntegerField(label='阅读量', required=False)
comment = serializers.IntegerField(label="评论量", required=False)
serializer不是只能为数据库模型类定义,也可以为非数据库模型类的数据定义。Serializer是独立于数据库之外的存在。
字段 | 字段构造方式 |
---|---|
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页面时,显示的字段帮助提示信息 |
定义好Serializer类后,就可以创建Serializer对象了。
Serializer的构造方法为:
Serializer(instance=None, data=empty, **kwarg)
说明:
用于序列化时,将模型类对象传入instance参数 模型数据--->Python字典--->Json、xml(用于输出,返回数据给前端)
用于反序列化时,将要被反序列化的数据传入data参数 前端发送的数据--->validate-->Python字典-->save、update-->模型类对象(用于输入,存入数据库)
除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如
serializer = AccountSerializer(account, context={'request': request})
# 先查询出一个图书对象
from books.models import BooksInfo
book = BooksInfo.objects.get(id=1)
# 构造序列化器对象
from books.serializers import BooksInfoSerializer
serializer = BooksInfoSerializer(book)
# 获取序列化数据
print(serializer.data)
# {'id': 1, 'title': '三国演义', 'pub_date': '1969-01-23', 'read': 0, 'comment': 0}
# 如果被序列化的是包含多条数据的,可以通过添加many=True参数补充说明
books = BooksInfo.objects.all()
serializer = BooksInfoSerializer(books, many=True)
print(serializer.data)
'''
[OrderedDict([('id', 1), ('title', '三国演义'), ('pub_date', '1969-01-23'), ('read', 0), ('comment', 0)]), OrderedDict([('id', 2), ('title', '西游记'), ('pub_date', '2021-08-02'), ('read', 0), ('comment', 0)])]
'''
如果需要序列化的数据中包含有其他关联对象,则对关联对象数据的序列化需要指明。
class HeroInfoSerializer(serializers.Serializer):
GENDER_CHOICES = (
(0, 'male'),
(1, 'female')
)
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(label='名称', max_length=20)
gender = serializers.ChoiceField(label='性别', default=0, choices=GENDER_CHOICES)
desc = serializers.CharField(label='描述信息', max_length=200, allow_null=True, required=False)
is_delete = serializers.BooleanField(default=False, label='逻辑删除', required=False)
对于关联字段,可以采用以下几种方式:
此字段将被序列化为关联对象的主键。
hero_book = serialziers.PrimaryKeyRelatedField(label='图书', read_only=True)
# 或
hero_book = serialziers.PrimaryKeyRelatedField(label='图书', queryset=BookInfo.objects.all())
指明该字段时需要包含read_only=True
或者queryset
参数:
例:
from books.models import HeroInfo, BooksInfo
from books.serialziers import HeroInfoSerializer, BooksInfoSerializer
hero = HeroInfo.objects.get(id=1)
serializer = HeroInfoSerializer(instance=hero)
serializer.data
# {'id': 1, 'name': '郭靖', 'gender': 1, 'desc': '降龙十八掌', 'is_delete': False, 'hero_book': 3}
此字段将被序列化为关联对象的字符串表示方式(即__str__方法
的返回值)
hero_book = serialziers.StringRelatedField(label='图书')
使用效果:
# {'id': 1, 'name': '郭靖', 'gender': 1, 'desc': '降龙十八掌', 'is_delete': False, 'hero_book': '天龙八部'}
此字段将被序列化为获取关联对象数据的接口连接
hero_book = serializers.HyperlinkedRelatedField(label='图书', read_only=True, view_name='book_detail')
view_name
参数指明关联URL的name
使用效果
class HeroDetailAPIView(APIView):
def get(self, request, pk):
hero = HeroInfo.objects.get(id=pk)
serialzier = HeroInfoSerializer(instance=hero, context={'request': request})
return Response(serialzier.data)
调用Serializer时要添加context
参数携带上request
。不然会报错
{"id":1,"name":"郭靖","gender":1,"desc":"降龙十八掌","is_delete":false,"hero_book":"http://127.0.0.1:8000/book/3/"}
此字段将被序列化为关联对象的指定字段数据
hero_book = serializers.SlugRelatedField(label='图书', read_only=True, slug_field='title')
slug_field
参数指明使用关联对象的哪个字段
使用效果:
{"id":1,"name":"郭靖","gender":1,"desc":"降龙十八掌","is_delete":false,"hero_book":"射雕英雄传"}
hero_book = BooksInfoSerializer()
使用效果
{"id":1,"name":"郭靖","gender":1,"desc":"降龙十八掌","is_delete":false,"hero_book":{"id":3,"title":"射雕英雄传","pub_date":"1980-05-01","read":12,"comment":34,"heroinfo_set":["郭靖","黄蓉","黄药师","欧阳锋","梅超风"]}}
序列化器的每个字段实际都是由该字段类型的to_representation
方法决定格式的,可以通过重写该方法来决定格式。
to_respresentation方法不仅局限在控制关联对象格式上,适用于各个序列化器字段类型。
自定义一个新的关联字段:
class BookRelateField(serializers.RelatedField):
def to_respresentation(self, value):
return f'{value.id}, {value.title}, {value.pub_date}'
指明hbook为BookRelateField类
hero_book = BookRelateField(read_only=True)
使用效果
{"id":1,"name":"郭靖","gender":1,"desc":"降龙十八掌","is_delete":false,"hero_book":"3, 射雕英雄传, 1980-05-01"}
如果关联的对象数据不是只有一个,而是包含多个数据,如果序列化图书数据,没个图书对象关联的人物对象可能有多个(一对多)。此时关联字段类型的指明仍可使用上述几种方式,只是在声明关联字段时,多补充一个many=True
参数即可。
使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。
流程:
is_valid()
方法进行验证,验证成功返回True,否则返回Falseerrors
属性获取错误信息,返回字典,包含了字段和字段的错误。validated_data
属性获取数据。示例:
from books.models import BooksInfo
from books.serialziers import BooksInfoSerializer
# 数据校验无误
data = {'title':'流畅的Python', 'pub_date':'2010-01-03'}
serializer = BooksInfoSerializer(data=data)
serializer.is_valid() # 返回True
serializer.errors # {}
serializer.validated_data # OrderedDict([('title', '流畅的Python'), ('pub_date', datetime.date(2010, 1, 3))])
# 数据校验失败
data = {'title':'流畅的Python'}
serializer = BooksInfoSerializer(data=data)
serializer.is_valid() # 返回False
serializer.errors # {'pub_date': [ErrorDetail(string='该字段是必填项。', code='required')]}
serializer.validated_data # {}
is_valid()
方法还可以在验证失败时抛出异常serializers.ValidationError
,可以通过传递raise_exception=True
参数开启,REST framework接收到此异常,会向前端返回响应码400。
对<field_name>
字段进行验证:
class BooksInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
...
def validate_title(self, value):
if 'python' not in value.lower():
raise serializers.ValidationError("图书不是关于python的")
return value
测试
from booktest.serializers import BooksInfoSerializer
data = {'btitle': 'august'}
serializer = BooksInfoSerializer(data=data)
serializer.is_valid() # False
serializer.errors
# {'btitle': [ErrorDetail(string='图书不是关于python的', code='invalid')]}
在序列化器中需要同时对多个字段进行比较验证时,可以覆写validate
方法来验证:
class BooksInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
...
def validate(self, attrs):
try:
if attrs['read'] < attrs['comment']:
raise ValidationError('评论量不能大于阅读量')
except KeyError:
return attrs
else:
return attrs
测试
from booktest.serializers import BookInfoSerializer
data = {'btitle': '流畅的Python', 'bread': 10, 'bcomment': 20}
s = BookInfoSerializer(data=data)
s.is_valid() # False
s.errors
# {'non_field_errors': [ErrorDetail(string='阅读量小于评论量', code='invalid')]}
也可以在attrs
添加键值对,以影响返回结果。查看serializer.validated_data
可以查看多出的键值对。
class BooksInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
...
def validate(self, attrs):
attrs['hello'] = "hello world"
return attrs
data = {'title':'流畅的Python','pub_date':'2010-10-23', 'read':12, 'comment':1}
serializer = BooksInfoSerializer(data=data)
serializer.is_valid() # True
serializer.validated_data
# OrderedDict([('title', '流畅的Python'), ('pub_date', datetime.date(2010, 10, 23)), ('read', 12), ('comment', 1), ('hello', 'hello world!')])
在字段中添加validators
选项参数,也可以补充验证行为:
def validate_read(value):
if int(value) < 0:
raise ValidationError('阅读量不能小于0')
return value
class BooksInfoSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(label='名称', max_length=20)
pub_date = serializers.DateField(label='发布日期')
read = serializers.IntegerField(label='阅读量', required=False, validators=(validate_read, ))
comment = serializers.IntegerField(label="评论量", required=False)
测试
from booktest.serializers import BookInfoSerializer
data = {"title": "流畅的Python", "pub_date": "1987-11-11", "read": -1, "comment": 24}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # False
serializer.errors
# {'read': [ErrorDetail(string='阅读量不能小于0', code='invalid')]}
三者的权重:
自定义验证器 > 单个字段的验证 > 多个字段的验证
如果在验证成功后,想要基于validated_data
完成数据对象的创建,可以通过覆写create()
和update()
两个方法来实现。
class BooksInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
...
def create(self, validated_data):
"""新建 validated_data:->字典"""
title = validated_data.get('title')
pub_date = validated_data.get('pub_date')
read = validated_data.setdefault('read', 0)
comment = validated_data.setdefault('comment', 0)
return BookInfo.objects.create(title=title, pub_date=pub_date, read=read, comment=comment)
# 如果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)
return instance
实现了上述两个方法后,在反序列化数据的时候,就可以通过save()方法返回一个数据对象实例了
book = serializer.save()
如果创建序列化器对象的时候,没有传递instance
实例,则调用save()
方法的时候,create()
被调用,相反,如果传递了instance
实例,则调用save()
方法的时候,update()
被调用。
1) 在对序列化器进行save()保存时,可以额外传递数据,这些数据可以在create()和update()中的validated_data参数获取到
serializer.save(owner=request.user)
2)默认序列化器必须传递所有required的字段,否则会抛出验证异常。但是我们可以使用partial参数来允许部分字段更新
# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)
基于Nginx+Supervisord+uWSGI+Django1.11.1+Python3.6.5构建