Django后端开发学习笔记Django REST Framework的序列化器
Posted 梆子井欢喜坨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django后端开发学习笔记Django REST Framework的序列化器相关的知识,希望对你有一定的参考价值。
学习参考:
【1】重写DRF的to_representation和to_internal_value方法有什么用途?
【2】Django REST Framework教程(4): 玩转序列化器(Serializer)
【3】DRF官方文档
目录
- 1. 重写DRF的to_representation和to_internal_value方法
- 2. 自定义序列化器类字段
- 3. 如何修改序列化器控制序列化后响应数据的输出格式?
- 4. 如何在反序列化时对客户端提供过来的数据进行验证(validation)?
- 5. 重写序列化器类自带的的create和update方法
1. 重写DRF的to_representation和to_internal_value方法
DRF所有序列化器类都继承了BaseSerializer 类, 通过重写该类的 to_representation() 和to_internal_value()方法可以改变序列化和反序列化的行为,比如给序列化后的数据添加额外的数据,或者对客户端API请求携带的数据进行反序列化处理以及用来自定义序列化器字段。
- to_representation() 允许改变序列化的输出。
- to_internal_value() 允许改变反序列化的输出。
1.1 to_representation方法
from rest_framework import serializers
from .models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
def to_representation(self, value):
# 调用父类获取当前序列化数据,value代表每个对象实例obj
# data是我们要输出的json数据,在python中为dict
data = super().to_representation(value)
# do something
return data
1.2 to_internal_value方法
to_internal_value主要在反序列化时用到,其作用处理API请求携带的数据,对其进行验证并转化为Python的数据类型。
from .models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
def to_internal_value(self, data):
# 提取所需要的数据,对其进行反序列化,data代表未验证的数据
article_data = data['article_data']
value = super().to_internal_value(article_data)
return value
2. 自定义序列化器类字段
to_representation()
和to_internal_value()
方法的令一个重要用途就是用来自定义序列化类字段。
下面是官方文档中的一个例子,数据库中存的是Color对象,但是序列化时要做一定的包装。
class Color:
"""
A color represented in the RGB colorspace.
"""
def __init__(self, red, green, blue):
assert(red >= 0 and green >= 0 and blue >= 0)
assert(red < 256 and green < 256 and blue < 256)
self.red, self.green, self.blue = red, green, blue
class ColorField(serializers.Field):
"""
Color对象会被序列化为 'rgb(#, #, #)'
"""
def to_representation(self, value):
return "rgb(%d, %d, %d)" % (value.red, value.green, value.blue)
def to_internal_value(self, data):
data = data.strip('rgb(').rstrip(')')
red, green, blue = [int(col) for col in data.split(',')]
return Color(red, green, blue)
默认情况下,字段值被视为映射到对象上的属性。如果需要自定义字段值的访问和设置方式,则需要重写.get_attribute()
或.get_value()
。
class ClassNameField(serializers.Field):
def get_attribute(self, instance):
# We pass the object instance onto `to_representation`,
# not just the field attribute.
return instance
def to_representation(self, value):
"""
Serialize the value's class name.
"""
return value.__class__.__name__
3. 如何修改序列化器控制序列化后响应数据的输出格式?
我们的Article模型和自定义的序列化器ArticleSerializer类分别如下所示。
# blog/models.py
class Article(models.Model):
"""Article Model"""
STATUS_CHOICES = (
('p', 'Published'),
('d', 'Draft'),
)
title = models.CharField(verbose_name='Title (*)', max_length=90, db_index=True)
body = models.TextField(verbose_name='Body', blank=True)
author = models.ForeignKey(User, verbose_name='Author', on_delete=models.CASCADE, related_name='articles')
status = models.CharField(verbose_name='Status (*)', max_length=1, choices=STATUS_CHOICES, default='s', null=True, blank=True)
create_date = models.DateTimeField(verbose_name='Create Date', auto_now_add=True)
def __str__(self):
return self.title
# blog/serializers.py
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
read_only_fields = ('id', 'author', 'create_date')
author外键序列化默认输出的是用户id,而不是用户名(username),status字段序列输出的也是我们存的"p"和"d",而不是可读的"Published"和"Draft",这样的输出不能很好地满足需求,因此我们需要修改序列化器控制序列化后响应数据的输出格式。
3.1 指定source
在序列化器中新建两个可读字段author和status字段,用以覆盖原来Article模型默认的字段,其中指定author字段的来源(source)为原单个author对象的username,status字段为get_status_display方法返回的完整状态。
class ArticleSerializer(serializers.ModelSerializer):
author = serializers.ReadOnlyField(source="author.username")
status = serializers.ReadOnlyField(source="get_status_display")
class Meta:
model = Article
fields = '__all__'
read_only_fields = ('id', 'author', 'create_date')
现在输出的结果符合要求,但进行增删改操作时会出问题。我们定义了一个仅可读的status字段把原来的status字段覆盖了,这样反序列化时用户将不能再对文章发表状态进行修改(原来的status字段是可读可修改的)。一个更好的方式是在ArticleSerializer中新增一个为full_status的可读字段,而不是简单覆盖原本可读可写的字段。(author字段原本就是只读字段,不受影响)
class ArticleSerializer(serializers.ModelSerializer):
author = serializers.ReadOnlyField(source="author.username")
full_status = serializers.ReadOnlyField(source="get_status_display")
class Meta:
model = Article
fields = '__all__'
read_only_fields = ('id', 'author', 'create_date')
3.2 使用SerializerMethodField自定义方法
如果进一步,想在输出的json数据中新增一些模型中原本没有的字段,可以使用SerializerMethodField,它可用于将任何类型的数据添加到对象的序列化表示中。
在序列化器中新建cn_status字段,格式为SerializerMethodField,然后再自定义一个get_cn_status方法输出文章中文发表状态即可。
class ArticleSerializer(serializers.ModelSerializer):
author = serializers.ReadOnlyField(source="author.username")
status = serializers.ReadOnlyField(source="get_status_display")
cn_status = serializers.SerializerMethodField()
class Meta:
model = Article
fields = '__all__'
read_only_fields = ('id', 'author', 'create_date')
def get_cn_status(self, obj):
if obj.status == 'p':
return "已发表"
elif obj.status == 'd':
return "草稿"
else:
return ''
SerializerMethodField通常用于显示模型中原本不存在的字段,类似可读字段,你不能通过反序列化对其直接进行修改。
3.3 使用嵌套序列化器
文章中的author字段实际上对应的是一个User模型实例化后的对象,既不是一个整数id,也不是用户名这样一个简单字符串,我们怎样显示更多用户对象信息呢? 其中一种解决方法是使用嵌套序列化器。
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email')
class ArticleSerializer(serializers.ModelSerializer):
author = UserSerializer(read_only=True)
class Meta:
model = Article
fields = '__all__'
read_only_fields = ('id', 'author', 'create_date')
展示效果如下所示:
上述三种方法可以和重写to_representation和to_internal_value方法配合使用,以更好得控制序列化输出。
4. 如何在反序列化时对客户端提供过来的数据进行验证(validation)?
在反序列化数据时,在尝试访问经过验证的数据或保存对象实例之前,总是需要调用 is_valid()
方法。如果发生任何验证错误,.errors
属性将包含表示结果错误消息的字典,如下所示:
serializer = CommentSerializer(data='email': 'foobar', 'content': 'baz')
serializer.is_valid()
# False
serializer.errors
# 'email': [u'Enter a valid e-mail address.'], 'created': [u'This field is required.']
字典中的每个键都是字段名称,值是与该字段对应的任何错误消息的字符串列表。non_field_errors 键也可能存在,并将列出任何常规验证错误。可以使用 REST framework 设置中的 NON_FIELD_ERRORS_KEY 来自定义 non_field_errors 键的名称。
当反序列化项目列表时,错误将作为表示每个反序列化项目的字典列表返回。
4.1 引发无效数据的异常 (Raising an exception on invalid data)
.is_valid() 方法使用可选的 raise_exception 标志,如果存在验证错误,将会抛出 serializers.ValidationError 异常。
这些异常由 REST framework 提供的默认异常处理程序自动处理,默认情况下将返回 HTTP 400 Bad Request 响应。
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)
4.2 字段级别验证 (Field-level validation)
您可以通过向您的 Serializer 子类中添加 .validate_<field_name>
方法来指定自定义字段级的验证。这些类似于 Django 表单中的 .clean_<field_name>
方法。这些方法采用单个参数,即需要验证的字段值。
您的 validate_<field_name>
方法应该返回已验证的值或抛出 serializers.ValidationError
异常。例如:
from rest_framework import serializers
class ArticleSerializer(serializers.Serializer):
title = serializers.CharField(max_length=100)
def validate_title(self, value):
"""
Check that the article is about Django.
"""
if 'django' not in value.lower():
raise serializers.ValidationError("Article is not about Django")
return value
注意:如果在您的序列化器上声明了 <field_name>
的参数为 required=False
,那么如果不包含该字段,则此验证步骤不会发生。
4.3 对象级别验证 (Object-level validation)
要执行需要访问多个字段的任何其他验证,请添加名为 .validate()
的方法到您的 Serializer 子类中。此方法采用单个参数,该参数是字段值的字典。如果需要,它应该抛出 ValidationError 异常,或者只返回经过验证的值。例如:
from rest_framework import serializers
class EventSerializer(serializers.Serializer):
description = serializers.CharField(max_length=100)
start = serializers.DateTimeField()
finish = serializers.DateTimeField()
def validate(self, data):
"""
Check that the start is before the stop.
"""
if data['start'] > data['finish']:
raise serializers.ValidationError("finish must occur after start")
return data
4.4 验证器 (Validators)
序列化器上的各个字段都可以包含验证器,通过在字段实例上声明,例如:
def multiple_of_ten(value):
if value % 10 != 0:
raise serializers.ValidationError('Not a multiple of ten')
class GameRecord(serializers.Serializer):
score = IntegerField(validators=[multiple_of_ten])
...
DRF还提供了很多可重用的验证器,比如UniqueValidator,UniqueTogetherValidator等等。通过在内部 Meta 类上声明来包含这些验证器,如下所示。下例中会议房间号和日期的组合必须要是独一无二的。
class EventSerializer(serializers.Serializer):
name = serializers.CharField()
room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
date = serializers.DateField()
class Meta:
# Each room only has one event per day.
validators = UniqueTogetherValidator(
queryset=Event.objects.all(),
fields=['room_number', 'date']
)
4.5 通过重写to_internal_value
方法实现
仍然以上面的ColorField类举例, .fail()
方法是引发serializers.ValidationError
的快捷方式,它从错误消息字典中获取消息字符串。
default_error_messages =
'incorrect_type': 'Incorrect type. Expected a string, but got input_type',
'incorrect_format': 'Incorrect format. Expected `rgb(#,#,#)`.',
'out_of_range': 'Value out of range. Must be between 0 and 255.'
def to_internal_value(self, data):
if not isinstance(data, str):
self.fail('incorrect_type', input_type=type(data).__name__)
if not re.match(r'^rgb\\([0-9]+,[0-9]+,[0-9]+\\)$', data):
self.fail('incorrect_format')
data = data.strip('rgb(').rstrip(')')
red, green, blue = [int(col) for col in data.split(',')]
if any([col > 255 or col < 0 for col in (red, green, blue)]):
self.fail('out_of_range')
return Color(red, green, blue)
5. 重写序列化器类自带的的create和update方法
当我们反序列化数据的时候,基于验证过的数据我们可以调用.save()方法返回一个对象实例。
comment = serializer.save()
调用.save()方法将创建新实例或者更新现有实例,具体取决于实例化序列化器类的时候是否传递了现有实例:
# .save() will create a new instance.
serializer = CommentSerializer(data=data)
# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
create()和update()方法定义了在调用serializer.save()时如何创建和修改完整的实例。
假设我们有个Profile模型与User模型是一对一的关系,当用户注册时我们希望把用户提交的数据分别存入User和Profile模型,这时我们就不得不重写序列化器自带的create方法了。
下例演示了如何通过一个序列化器创建两个模型对象。
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ('username', 'email', 'profile')
def create(self, validated_data):
profile_data = validated_data.pop('profile')
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, **profile_data)
return user
同时更新两个关联模型实例时也同样需要重写update方法。
def update(self, instance, validated_data):
profile_data = validated_data.pop('profile')
# 除非应用程序正确地强制始终设置该字段,否则就应该抛出一个需要处理的`DoesNotExist`。
profile = instance.profile
instance.username = validated_data.get('username', instance.username)
instance.email = validated_data.get('email', instance.email)
instance.save()
profile.is_premium_member = profile_data.get(
'is_premium_member',
profile.is_premium_member
)
profile.has_support_contract = profile_data.get(
'has_support_contract',
profile.has_support_contract
)
profile.save()
return instance
因为序列化器使用嵌套后,创建和更新的行为可能不明确,并且可能需要相关模型之间的复杂依赖关系,REST framework 3 要求你始终显式的编写这些方法。默认的 ModelSerializer .create()
和 .update()
方法不包括对可写嵌套表示的支持,所以我们总是需要对create和update方法进行重写。
以上是关于Django后端开发学习笔记Django REST Framework的序列化器的主要内容,如果未能解决你的问题,请参考以下文章
Django后端开发学习笔记Django REST Framework基于类的视图
Django后端开发学习笔记Django REST Framework基于类的视图
Django后端开发学习笔记Django REST Framework的序列化器
Django后端开发学习笔记Django REST Framework的序列化器