August Rush

一个还在努力成长的小火汁!

游龙当归海,海不迎我自来也。

We create our own demons.

You can reach me at augustrush0923@gmail.com
DRF-Serializer序列化器
发布:2021年08月24日 | 作者:augustrush | 阅读量: 966

序列化器的作用

  1. 对数据进行转换 序列化(输出、read_only)和反序列化(输入、write_only)

  2. 进行数据的校验

判断用户发送的数据是否合法(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的构造方法为:

Serializer(instance=None, data=empty, **kwarg)

说明:

  1. 用于序列化时,将模型类对象传入instance参数 模型数据--->Python字典--->Json、xml(用于输出,返回数据给前端)

  2. 用于反序列化时,将要被反序列化的数据传入data参数 前端发送的数据--->validate-->Python字典-->save、update-->模型类对象(用于输入,存入数据库)

  3. 除了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)

对于关联字段,可以采用以下几种方式:


PrimaryKeyRelatedField

此字段将被序列化为关联对象的主键

hero_book = serialziers.PrimaryKeyRelatedField(label='图书', read_only=True)
# 或
hero_book = serialziers.PrimaryKeyRelatedField(label='图书', queryset=BookInfo.objects.all())

指明该字段时需要包含read_only=True或者queryset参数:

  • 包含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}


StringRelatedField

此字段将被序列化为关联对象的字符串表示方式(即__str__方法的返回值)

hero_book = serialziers.StringRelatedField(label='图书')

使用效果:

# {'id': 1, 'name': '郭靖', 'gender': 1, 'desc': '降龙十八掌', 'is_delete': False, 'hero_book': '天龙八部'}


HyperlinkedRelatedField

此字段将被序列化为获取关联对象数据的接口连接

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/"}


SlugRelatedField

此字段将被序列化为关联对象的指定字段数据

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_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,否则返回False
  • 验证失败,可以通过序列化器对象的errors属性获取错误信息,返回字典,包含了字段和字段的错误。
  • 验证成功,可以通过序列化器对象的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。


自定义验证


valiedate_\<field_name>

<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

在序列化器中需要同时对多个字段进行比较验证时,可以覆写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

在字段中添加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构建

京ICP备20007446号-1 & 豫公网安备 41100202000460号

网站地图 & RSS | Feed