Django REST framework 3.0

Django REST framework 的 3.0 版本是近四年迭代和改进的结果。它全面地解决了序列化器、字段和通用视图中先前遗留的一些设计问题。

这个版本本质上是递增的。有一些突破性的 API 更改,升级需要您仔细阅读发布说明,但是迁移路径应该相对简单。

REST franewirk API 和实现的质量差异应该使编写,维护和调试应用程序变得更加容易。

3.0 是我们最近的 Kickstarter 活动资助的三个版本中的第一个。

一如既往,非常感谢我们众多精彩的赞助商。如果您正在寻找 Django 演出,并希望与聪明的有社区意识的人工作,你应该查看该列表,看看谁在招聘。


新特性 (New features)

这个新版本的显著特点包括:

  • 序列化器上的可打印表示,它允许您检查实例上到底有哪些字段。
  • 简单的模型序列化器,它们更容易理解和调试,并且可以在隐式 ModelSerializer 类和显式 Serializer 类之间轻松切换。
  • 新的 BaseSerializer 类,使编写用于替代存储后端的序列化器或完全自定义您的序列化和验证逻辑变得更容易。
  • 一个更干净的字段 API,包括新类,如 ListFieldMultipleChoiceField
  • 通用视图的超简单默认实现
  • 支持覆盖 API 如何处理验证错误。
  • 元数据 API 允许您自定义 API 如何处理 OPTIONS 请求。
  • 默认情况下,启用了 unicode 样式编码的更紧凑的 JSON 输出。
  • 用于序列化器的基于模板的 HTML 表单渲染。这将在即将发布的 3.1 版本中最终敲定为公共 API。

3.1 和 3.2 版本继续计划重要的新功能。这些版本将对应两个 Kickstarter 延伸目标 - “功能改进” 和 “管理界面”。进一步的 3.x 版本将提供简单的升级,而没有 3.0 版本所需的基本 API 更改的相同级别。


REST framework:在引擎盖下 (REST framework: Under the hood.)

2014 年 11 月在阿姆斯特丹举行的 Django:在引擎盖下事件的演讲,提供了一些关于 3.0 背后的设计决策的良好背景信息。


以下是有关 3.0 的 API 更改和迁移说明的深入指南。

请求对象 (Request objects)

The .data and .query_params properties.

request.DATArequest.FILES 的用法现在处于等待弃用状态,有助于单个 request.data 属性包含所有已解析的数据。

对于只解析 url 编码或多部分请求的 web 应用程序来说,拥有单独的属性是合理的,但是对于 REST framework 支持的通用请求解析来说就没有什么意义了。

您现在可以将所有请求数据传递到单个参数中的序列化程序类:

  1. # Do this...
  2. ExampleSerializer(data=request.data)

而不是单独传递 files 参数:

  1. # Don't do this...
  2. ExampleSerializer(data=request.DATA, files=request.FILES)

request.QUERY_PARAMS 的用法现在正在等待弃用,以支持小写的 request.query_params


序列化器 (Serializers)

单步对象创建 (Single-step object creation.)

以前,序列化器使用两步对象创建,如下所示:

  1. 验证数据将创建对象实例。此实例可用作 serializer.object
  2. 然后调用 serializer.save() 将对象实例保存到数据库。

这种风格与 Django 中的 ModelForm 类的工作方式一致,但由于多种原因而存在问题:

  • 某些数据 (例如多对多关系) 保存之前无法添加到对象实例中。这种类型的数据需要隐藏在对象实例的某个未记录的状态中,或者作为序列化器实例保存状态,以便在调用 .save() 时使用。
  • 直接实例化模型实例意味着您不能使用模型管理器类来创建实例,例如 ExampleModel.objects.create(...)。Manager 类是强制执行业务逻辑和应用程序级数据约束的优秀层。
  • 这两步过程使得它不清楚在何处放置反序列化逻辑。例如,在创建对象期间或在对象保存期间,是否应将额外属性 (如当前用户) 加到实例中?

我们现在使用单步对象创建,如下所示:

  1. 验证数据使已清理的数据作为 serializer.validated_data 可用。
  2. 调用 serializer.save() 然后保存并返回新的对象实例。

由此产生的 API 更改将在下面进一步详述。

The .create() and .update() methods.

现在删除了 .restore_object() 方法,我们有了两个单独的方法,create().update()。这些方法与之前的 .restore_object() 略有不同。

使用 .create().update() 方法时,您应该创建并保存对象实例。这与前面的 .restore_object() 行为形成了对比,后者会实例化对象,但不会保存它。

这些方法还替换了不再存在的可选 .save_object() 方法。

本教程中的以下示例以前使用 restore_object() 来处理创建和更新对象实例。

  1. def restore_object(self, attrs, instance=None):
  2. if instance:
  3. # 更新现有实例
  4. instance.title = attrs.get('title', instance.title)
  5. instance.code = attrs.get('code', instance.code)
  6. instance.linenos = attrs.get('linenos', instance.linenos)
  7. instance.language = attrs.get('language', instance.language)
  8. instance.style = attrs.get('style', instance.style)
  9. return instance
  10. # 创建新实例
  11. return Snippet(**attrs)

现在将分为两种不同的方法。

  1. def update(self, instance, validated_data):
  2. instance.title = validated_data.get('title', instance.title)
  3. instance.code = validated_data.get('code', instance.code)
  4. instance.linenos = validated_data.get('linenos', instance.linenos)
  5. instance.language = validated_data.get('language', instance.language)
  6. instance.style = validated_data.get('style', instance.style)
  7. instance.save()
  8. return instance
  9. def create(self, validated_data):
  10. return Snippet.objects.create(**validated_data)

请注意,这些方法应返回新创建的对象实例。

使用 .validated_data 而不是 .object (Use .validated_data instead of .object.)

如果需要在保存之前检查数据,而不是使用不再存在的 .object 属性,则现在必须使用 .validated_data 属性。

例如,以下代码不再有效:

  1. if serializer.is_valid():
  2. name = serializer.object.name # 检查经过验证的字段数据。
  3. logging.info('Creating ticket "%s"' % name)
  4. serializer.object.user = request.user # 保存时包括用户。
  5. serializer.save()

您现在可以使用 .validated_data 来检查已清理的传入值,而不是使用 .object 来检查部分构造的实例。另外,您不能直接在实例上设置额外的属性,而是将它们作为关键字参数传递给 .save() 方法。

相应的代码现在看起来像这样:

  1. if serializer.is_valid():
  2. name = serializer.validated_data['name'] # 检查经过验证的字段数据。
  3. logging.info('Creating ticket "%s"' % name)
  4. serializer.save(user=request.user) # 保存时包括用户。

Using .is_valid(raise_exception=True)

is_valid() 方法现在接受一个可选的布尔标志 raise_exception

如果序列化器数据包含验证错误,则调用 .is_valid(raise_exception = True) 将导致引发 ValidationError。这个错误将由 REST framework 的默认异常处理程序处理,允许您从视图代码中删除错误响应处理。

通过使用 EXCEPTION_HANDLER 设置键,可以对错误响应的处理和格式化进行全局更改。

此更改还意味着现在可以更改内置通用视图使用的错误响应的样式,而不必包含 mixin 类或其他覆盖。

Using serializers.ValidationError.

以前 serializers.ValidationError 错误只是 django.core.exceptions.ValidationError 的同义词。现在已经改变了,以便它继承标准的 APIException 基类。

这背后的原因是 Django 的 ValidationError 类是用于 HTML 表单的,并且它的 API 使得它在使用序列化器中可能出现的嵌套验证错误时稍微有些尴尬。

对于大多数用户来说,这种更改不需要对代码库进行任何更新,但是值得确保的是,无论何时出现验证错误,您都应该使用 serializers.ValidationError 异常类,而不是 Django 内置的异常。

我们强烈建议您使用命名空间导入 import serializers 样式,而不是 from serializers import ValidationError,以避免任何潜在的混淆。

更改为 validate_ <field_name> (Change to validate_<field_name>.)

可以附加到序列化器类上的 validate_<field_name> 钩子会稍微改变它们的签名并返回类型。在此之前,这些将获取所有传入数据的字典,以及表示字段名称的键,并返回包含该字段的经过验证的数据的字典:

  1. def validate_score(self, attrs, source):
  2. if attrs['score'] % 10 != 0:
  3. raise serializers.ValidationError('This field should be a multiple of ten.')
  4. return attrs

现在稍微简化了一下,方法钩子只是简单地获取要验证的值,并返回验证值。

  1. def validate_score(self, value):
  2. if value % 10 != 0:
  3. raise serializers.ValidationError('This field should be a multiple of ten.')
  4. return value

任何适用于多个字段的特设验证都应该像往常一样放在 .validate(self, attrs) 方法中。

因为 .validate_<field_name> 之前会接受完整的属性字典,所以它可用于根据另一个字段中的输入来验证字段。现在,如果您需要这样做,您应该使用 .validate() 代替。

您可以通过引发简单的 ValidationError 从验证方法返回 non_field_errors

  1. def validate(self, attrs):
  2. # serializer.errors == {'non_field_errors': ['A non field error']}
  3. raise serializers.ValidationError('A non field error')

或者,如果希望错误针对特定字段,那么在实例化 ValidationError 时使用字典,如下所示:

  1. def validate(self, attrs):
  2. # serializer.errors == {'my_field': ['A field error']}
  3. raise serializers.ValidationError({'my_field': 'A field error'})

这确保您仍然可以编写比较所有输入字段的验证,但这会根据特定字段标记错误。

删除 transform_<field_name> (Removal of transform_<field_name>.)

不再提供序列化器类上未充分利用的 transform_ <field_name>。相反,如果需要对表示样式应用任何修改,您应该覆盖 to_representation()

举个栗子:

  1. def to_representation(self, instance):
  2. ret = super(UserSerializer, self).to_representation(instance)
  3. ret['username'] = ret['username'].lower()
  4. return ret

去掉 API 的额外点意味着现在只有一种正确的方法。这有助于重复和强化核心 API,而不是采用多种不同的方法。

如果您绝对需要保留 transform_<field_name> 行为,例如,为了提供更简单的 2.x 到 3.0 升级,您可以使用 mixin 或 serializer 基类来重新添加行为。例如:

  1. class BaseModelSerializer(ModelSerializer):
  2. """
  3. 自定义的 ModelSerializer 类,它保留 2.x 样式 `transform_<field_name>` 行为。
  4. """
  5. def to_representation(self, instance):
  6. ret = super(BaseModelSerializer, self).to_representation(instance)
  7. for key, value in ret.items():
  8. method = getattr(self, 'transform_' + key, None)
  9. if method is not None:
  10. ret[key] = method(value)
  11. return ret

ModelSerializer 验证和 ModelForm 之间的差异。 (Differences between ModelSerializer validation and ModelForm.)

此更改还意味着我们不再在模型实例上使用 .full_clean() 方法,而是在序列化器上显式地执行所有验证。这提供了更清晰的分离,并确保在 ModelSerializer 类上没有自动验证行为,而这些行为在常规 Serializer 类上也无法轻易复制。

在大多数情况下,这种变化应该是透明的。字段验证和唯一性检查仍将正常运行,但实现稍有不同。

需要注意的一个区别是 .clean() 方法不会作为序列化器验证的一部分调用,就像使用 ModelForm 那样。使用序列化器 .validate() 方法在需要时对传入数据执行最后的验证步骤。

在某些情况下,您确实需要在模型 .clean() 方法中保留验证逻辑,而不能将其分离到序列化器 .validate() 中。您可以通过在 .validate() 方法中显式地实例化一个模型实例来实现这一点。

  1. def validate(self, attrs):
  2. instance = ExampleModel(**attrs)
  3. instance.clean()
  4. return attrs

同样,如果可能的话,你真的应该考虑将验证逻辑正确地从模型方法中分离出来,但上述内容在某些向后兼容性情况下可能很有用,或者对于简单的迁移路径也很有用。

可写嵌套序列化 (Writable nested serialization.)

REST framework 2.x 尝试自动支持可写嵌套序列化,但行为复杂且不明显。尝试自动处理这些情况是有问题的:

  • 保存多个相关模型实例的顺序可能涉及复杂的依赖关系。
  • 当相关的模型传递 None 数据时,用户应该期望什么行为是不清楚的。
  • 目前还不清楚用户应该如何期望多个关系来处理多个记录的更新、创建和删除。

现在,使用 ModelSerializer 上的 depth 选项将默认创建只读嵌套序列化器

如果您尝试使用可写嵌套的序列化器,而不编写自定义的 create() 和/或 update() 方法,则在尝试保存序列化器时将看到断言错误。例如:

  1. >>> class ProfileSerializer(serializers.ModelSerializer):
  2. >>> class Meta:
  3. >>> model = Profile
  4. >>> fields = ('address', 'phone')
  5. >>>
  6. >>> class UserSerializer(serializers.ModelSerializer):
  7. >>> profile = ProfileSerializer()
  8. >>> class Meta:
  9. >>> model = User
  10. >>> fields = ('username', 'email', 'profile')
  11. >>>
  12. >>> data = {
  13. >>> 'username': 'lizzy',
  14. >>> 'email': 'lizzy@example.com',
  15. >>> 'profile': {'address': '123 Acacia Avenue', 'phone': '01273 100200'}
  16. >>> }
  17. >>>
  18. >>> serializer = UserSerializer(data=data)
  19. >>> serializer.save()
  20. AssertionError: The `.create()` method does not support nested writable fields by default. Write an explicit `.create()` method for serializer `UserSerializer`, or set `read_only=True` on nested serializer fields.

要使用可写嵌套序列化,您需要在序列化器类上声明嵌套字段,并显式地编写 create() 和/或 update() 方法。

  1. class UserSerializer(serializers.ModelSerializer):
  2. profile = ProfileSerializer()
  3. class Meta:
  4. model = User
  5. fields = ('username', 'email', 'profile')
  6. def create(self, validated_data):
  7. profile_data = validated_data.pop('profile')
  8. user = User.objects.create(**validated_data)
  9. Profile.objects.create(user=user, **profile_data)
  10. return user

单步对象的创建比以前 .restore_object() 行为更简单,也更明显。

可打印的序列化器表示 (Printable serializer representations.)

序列化器实例现在支持可打印表示,允许您检查实例上存在的字段。

例如,给出以下示例模型:

  1. class LocationRating(models.Model):
  2. location = models.CharField(max_length=100)
  3. rating = models.IntegerField()
  4. created_by = models.ForeignKey(User)

让我们创建一个对应于 LocationRating 模型的简单 ModelSerializer 类。

  1. class LocationRatingSerializer(serializer.ModelSerializer):
  2. class Meta:
  3. model = LocationRating

我们现在可以使用 python manage.py shell 检查 Django shell 中的序列化器表示…

  1. >>> serializer = LocationRatingSerializer()
  2. >>> print(serializer) # 或者在 Python 2.x 中使用 `print serializer`
  3. LocationRatingSerializer():
  4. id = IntegerField(label='ID', read_only=True)
  5. location = CharField(max_length=100)
  6. rating = IntegerField()
  7. created_by = PrimaryKeyRelatedField(queryset=User.objects.all())

The extra_kwargs option.

ModelSerializer 上的 write_only_fields 选项已移至 PendingDeprecation,并替换为更通用的 extra_kwargs

  1. class MySerializer(serializer.ModelSerializer):
  2. class Meta:
  3. model = MyModel
  4. fields = ('id', 'email', 'notes', 'is_admin')
  5. extra_kwargs = {
  6. 'is_admin': {'write_only': True}
  7. }

或者,在序列化器类上显式指定该字段:

  1. class MySerializer(serializer.ModelSerializer):
  2. is_admin = serializers.BooleanField(write_only=True)
  3. class Meta:
  4. model = MyModel
  5. fields = ('id', 'email', 'notes', 'is_admin')

对于更常见的情况,read_only_fields 选项仍然是方便的快捷方式。

更改为 HyperlinkedModelSerializer (Changes to HyperlinkedModelSerializer.)

view_namelookup_field 选项已移至 PendingDeprecation。它们不再是必需的,因为您可以使用 extra_kwargs 参数:

  1. class MySerializer(serializer.HyperlinkedModelSerializer):
  2. class Meta:
  3. model = MyModel
  4. fields = ('url', 'email', 'notes', 'is_admin')
  5. extra_kwargs = {
  6. 'url': {'lookup_field': 'uuid'}
  7. }

或者,在序列化器类上显式指定该字段:

  1. class MySerializer(serializer.HyperlinkedModelSerializer):
  2. url = serializers.HyperlinkedIdentityField(
  3. view_name='mymodel-detail',
  4. lookup_field='uuid'
  5. )
  6. class Meta:
  7. model = MyModel
  8. fields = ('url', 'email', 'notes', 'is_admin')

模型方法和属性的字段。 (Fields for model methods and properties.)

使用 ModelSerializer,您现在可以在字段选项中指定引用模型方法或属性的字段名。例如,假设您有以下模型:

  1. class Invitation(models.Model):
  2. created = models.DateTimeField()
  3. to_email = models.EmailField()
  4. message = models.CharField(max_length=1000)
  5. def expiry_date(self):
  6. return self.created + datetime.timedelta(days=30)

您可以在 ModelSerializer 类中包含 expiry_date 作为字段选项。

  1. class InvitationSerializer(serializers.ModelSerializer):
  2. class Meta:
  3. model = Invitation
  4. fields = ('to_email', 'message', 'expiry_date')

这些字段将映射到 serializers.ReadOnlyField() 实例。

  1. >>> serializer = InvitationSerializer()
  2. >>> print repr(serializer)
  3. InvitationSerializer():
  4. to_email = EmailField(max_length=75)
  5. message = CharField(max_length=1000)
  6. expiry_date = ReadOnlyField()

The ListSerializer class.

现在已添加 ListSerializer 类,并允许您创建仅用于接受多个输入的基本序列化器类。

  1. class MultipleUserSerializer(ListSerializer):
  2. child = UserSerializer()

您还可以对序列化器类使用 many = True 参数。值得注意的是,many=True 参数透明地创建了 ListSerializer 实例,允许在 REST framework 代码库中干净地分隔列表和非列表数据的验证逻辑。

您通常希望继续使用现有的 many=True 标志,而不是显式地声明 ListSerializer 类,但是如果您需要批量更新编写的自定义 createupdate 方法,或者提供其他自定义行为,那么显式地声明类是非常有用的。

请参阅新的 ListField 类,它以相同的方式验证输入,但不包括 .is_valid().data.save() 等序列化器接口。

The BaseSerializer class.

REST framework 现在包含一个简单的 BaseSerializer 类,可以用来轻松地支持替代序列化和反序列化样式。

该类实现与 Serializer 类相同的基本 API:

  • .data - 返回传出原生表示。
  • .is_valid() - 反序列化和验证传入的数据。
  • .validated_data - 返回经过验证的传入数据。
  • .errors - 在验证期间返回错误。
  • .save() - 将验证过的数据持久化到对象实例中。

可以覆盖四种方法,具体取决于您希望序列化器类支持的功能:

  • .to_representation() - 重写这个以支持序列化,用于读取操作。
  • .to_internal_value() - 重写这个以支持反序列化,用于写操作。
  • .create().update() - 覆盖其中的任意一个或两个,以支持保存实例。

因为此类提供与 Serializer 类相同的接口,所以您可以将它与现有的基于类的通用视图一起使用,就像使用常规 SerializerModelSerializer 一样。

在执行此操作时您将注意到的唯一区别是 BaseSerializer 类不会在可浏览的 API 中生成 HTML 表单。这是因为它们返回的数据不包括允许将每个字段渲染为合适的 HTML 输入的所有字段信息。

只读 BaseSerializer 类 (Read-only BaseSerializer classes.)

要使用 BaseSerializer 类实现只读序列化器,只需重写 .to_representation() 方法。让我们看一个使用简单 Django 模型的示例:

  1. class HighScore(models.Model):
  2. created = models.DateTimeField(auto_now_add=True)
  3. player_name = models.CharField(max_length=10)
  4. score = models.IntegerField()

创建只读序列化器以将 HighScore 实例转换为原始数据类型非常简单。

  1. class HighScoreSerializer(serializers.BaseSerializer):
  2. def to_representation(self, obj):
  3. return {
  4. 'score': obj.score,
  5. 'player_name': obj.player_name
  6. }

我们现在可以使用此类来序列化单个 HighScore 实例:

  1. @api_view(['GET'])
  2. def high_score(request, pk):
  3. instance = HighScore.objects.get(pk=pk)
  4. serializer = HighScoreSerializer(instance)
  5. return Response(serializer.data)

或者使用它来序列化多个实例:

  1. @api_view(['GET'])
  2. def all_high_scores(request):
  3. queryset = HighScore.objects.order_by('-score')
  4. serializer = HighScoreSerializer(queryset, many=True)
  5. return Response(serializer.data)
读写 BaseSerializer 类 (Read-write BaseSerializer classes.)

要创建读写序列化器,我们首先需要实现 .to_internal_value() 方法。此方法返回将用于构造对象实例的验证值,如果提供的数据格式不正确,则可能引发 ValidationError

一旦实现了 .to_internal_value(),基本的验证 API 将在序列化器上可用,您将能够使用 .is_valid().validated_data.errors

如果还想支持 .save(),还需要实现 .create().update() 方法中的任意一种或两者。

下面是我们以前的 HighScoreSerializer 的完整示例,它已经被更新以支持读写操作。

  1. class HighScoreSerializer(serializers.BaseSerializer):
  2. def to_internal_value(self, data):
  3. score = data.get('score')
  4. player_name = data.get('player_name')
  5. # 执行数据验证。
  6. if not score:
  7. raise ValidationError({
  8. 'score': 'This field is required.'
  9. })
  10. if not player_name:
  11. raise ValidationError({
  12. 'player_name': 'This field is required.'
  13. })
  14. if len(player_name) > 10:
  15. raise ValidationError({
  16. 'player_name': 'May not be more than 10 characters.'
  17. })
  18. # 返回经过验证的值。这将作为 `.validated_data` 属性。
  19. return {
  20. 'score': int(score),
  21. 'player_name': player_name
  22. }
  23. def to_representation(self, obj):
  24. return {
  25. 'score': obj.score,
  26. 'player_name': obj.player_name
  27. }
  28. def create(self, validated_data):
  29. return HighScore.objects.create(**validated_data)

使用 BaseSerializer 创建新的通用序列化器 (Creating new generic serializers with BaseSerializer.)

如果您想要实现新的通用序列化器类来处理特定的序列化样式或与替代存储后端集成,那么 BaseSerializer 类也很有用。

以下类是可以处理将任意对象强制转换为基本表示的通用序列化器的示例。

  1. class ObjectSerializer(serializers.BaseSerializer):
  2. """
  3. 将任意复杂对象强制转换为基本表示形式的只读序列化器。
  4. """
  5. def to_representation(self, obj):
  6. for attribute_name in dir(obj):
  7. attribute = getattr(obj, attribute_name)
  8. if attribute_name('_'):
  9. # 忽略私有属性。
  10. pass
  11. elif hasattr(attribute, '__call__'):
  12. # 忽略方法和其他 callables。
  13. pass
  14. elif isinstance(attribute, (str, int, bool, float, type(None))):
  15. # 基本类型可以不经修改地传递。
  16. output[attribute_name] = attribute
  17. elif isinstance(attribute, list):
  18. # 递归处理列表中的项。
  19. output[attribute_name] = [
  20. self.to_representation(item) for item in attribute
  21. ]
  22. elif isinstance(attribute, dict):
  23. # 递归处理词典中的项。
  24. output[attribute_name] = {
  25. str(key): self.to_representation(value)
  26. for key, value in attribute.items()
  27. }
  28. else:
  29. # 强制其他任何字符串表示。
  30. output[attribute_name] = str(attribute)

序列化器字段 (Serializer fields)

FieldReadOnly 字段类 (The Field and ReadOnly field classes.)

对于字段基类有一些小的调整。

以前我们有这两个基类:

  • Field 作为只读字段的基类。包含用于序列化数据的默认实现。
  • WritableField 作为读写字段的基类。

我们现在使用以下内容:

  • Field 是所有字段的基类。它不包括序列化或反序列化数据的任何默认实现。
  • ReadOnlyField 是只读字段的具体实现,它只返回属性值而不进行修改。

The required, allow_null, allow_blank and default arguments.

REST framework 现在对字段的空值验证有更明确和清晰的控制。

以前,required=False 关键字参数的含义未被指定。在实践中,它的使用意味着一个字段既可以不包含在输入中,也可以包含在输入中,但可以是 None 或空字符串。

现在我们有了更好的分离,具有单独的 requiredallow_nullallow_blank 参数。

下面一组参数用于控制空值的验证:

  • required=False:该值不需要出现在输入中,如果没有看到该值,则不会传递给 .create().update()
  • default=<value>:该值不需要出现在输入中,如果没有看到,则会将默认值传递给 .create().update()
  • allow_null=TrueNone 是有效输入。
  • allow_blank=True'' 是有效输入。仅适用于 CharField 和子类。

如果相应的模型字段有默认值,通常需要使用 required=False,并且如果需要,还需要设置 allow_null=Trueallow_blank=True

default 参数也是可用的,并且总是意味着字段不需要在输入中。没有必要在指定默认值时使用 required 参数,这样做将导致错误。

强制输出类型 (Coercing output types.)

以前的字段实现在很多情况下并没有强制将值返回到正确的类型。例如,如果属性值是字符串,IntegerField 将返回字符串输出。现在我们更加严格地强制正确的返回类型,导致更多的约束和预期的行为。

Removal of .validate().

.validate() 方法现在从字段类中删除了。这个方法在任何情况下都是没有文档说明的,而不是公共的 API。相反,您应该简单地重写 to_internal_value()

  1. class UppercaseCharField(serializers.CharField):
  2. def to_internal_value(self, data):
  3. value = super(UppercaseCharField, self).to_internal_value(data)
  4. if value != value.upper():
  5. raise serializers.ValidationError('The input should be uppercase only.')
  6. return value

以前的验证错误可能会在 .to_native().validate() 中引发,这使得应该使用哪个不太明显。仅提供单点 API 可确保更多重复和强化核心 API。

The ListField class.

ListField 类现在已经添加了。该字段验证列表输入。它接受 child 关键字参数,该参数用于指定用于验证列表中的每个项的字段。例如:

  1. scores = ListField(child=IntegerField(min_value=0, max_value=100))

您还可以使用声明式样式来创建 ListField 的新子类,如下所示:

  1. class ScoresField(ListField):
  2. child = IntegerField(min_value=0, max_value=100)

现在我们可以在另一个序列化器中使用 ScoresField 类:

  1. scores = ScoresField()

请参阅新的 ListSerializer 类,该类以相同的方式验证输入,但也包括 .is_valid().data.save() 等序列化器接口。

The ChoiceField class may now accept a flat list.

ChoiceField 类现在除了使用一对列表 (name, display_value) 的现有样式外,还可以接受选择列表。以下内容现在有效:

  1. color = ChoiceField(choices=['red', 'green', 'blue'])

The MultipleChoiceField class.

已经添加了 MultipleChoiceField 类。此字段的作用类似 ChoiceField,但返回一个集合,其中可能不包含 none、一个或多个有效选项。

更改自定义字段 API (Changes to the custom field API.)

from_native(self, value)to_native(self, data) 方法名已经被更明显命名的 to_internal_value(self, data)to_representation(self, value) 所取代。

删除 field_from_native()field_to_native() 方法。以前,如果您想不只是从对象中查找字段值的方式自定义行为,则可以使用这些方法。例如…

  1. def field_to_native(self, obj, field_name):
  2. """一个返回类名的自定义只读字段。"""
  3. return obj.__class__.__name__

现在,如果您需要访问整个对象,则需要覆盖以下一个或两个:

  • 使用 get_attribute 修改传递给 to_representation() 的属性值。
  • 使用 get_value 修改传递给 to_internal_value() 的数据值。

例如:

  1. def get_attribute(self, obj):
  2. # 将整个对象传递到 `to_representation()`,而不是标准的属性查找。
  3. return obj
  4. def to_representation(self, value):
  5. return value.__class__.__name__

关系字段需要显式 queryset (Explicit queryset required on relational fields.)

以前在序列化器类上显式声明的关系字段可以省略 queryset 参数,如果 (且仅当) 在 ModelSerializer 上声明它们。

此代码在 2.4.3 中有效:

  1. class AccountSerializer(serializers.ModelSerializer):
  2. organizations = serializers.SlugRelatedField(slug_field='name')
  3. class Meta:
  4. model = Account

但是这段代码在 3.0 中无效:

  1. # 缺少 `queryset`
  2. class AccountSerializer(serializers.Serializer):
  3. organizations = serializers.SlugRelatedField(slug_field='name')
  4. def restore_object(self, attrs, instance=None):
  5. # ...

现在对于可写的关系字段总是需要 queryset 参数。这消除了一些魔力,使在隐式 ModelSerializer 类和显式 Serializer 类之间切换变得更容易、更明显。

  1. class AccountSerializer(serializers.ModelSerializer):
  2. organizations = serializers.SlugRelatedField(
  3. slug_field='name',
  4. queryset=Organization.objects.all()
  5. )
  6. class Meta:
  7. model = Account

queryset 参数只对可写字段是必需的,对于 read_only=True 的字段则不是必需的或有效的。

SerializerMethodField 的可选参数 (Optional argument to SerializerMethodField.)

SerializerMethodField 的参数现在是可选的,默认值是 get_<field_name>。例如以下是有效的:

  1. class AccountSerializer(serializers.Serializer):
  2. # `method_name='get_billing_details'` by default.
  3. billing_details = serializers.SerializerMethodField()
  4. def get_billing_details(self, account):
  5. return calculate_billing(account)

为了确保代码样式一致,如果包含与默认方法名称匹配的冗余方法名称参数,就会引发断言错误。例如,以下代码将引发错误:

  1. billing_details = serializers.SerializerMethodField('get_billing_details')

强制执行一致 source 用法 (Enforcing consistent source usage.)

我看到了一些不必要地包含 source 参数的代码库,将其设置为与字段名相同的值。这种用法是冗余和混乱的,使得通常不需要 source 的情况不那么明显。

以下用法现在会引发错误:

  1. email = serializers.EmailField(source='email')

The UniqueValidator and UniqueTogetherValidator classes.

REST framework 现在提供了新的验证器,允许您确保字段唯一性,同时仍然使用完全显式的 Serializer 类,而不是使用 ModelSerializer

UniqueValidator 应该应用于序列化器字段,并接受一个 queryset 参数。

  1. from rest_framework import serializers
  2. from rest_framework.validators import UniqueValidator
  3. class OrganizationSerializer(serializers.Serializer):
  4. url = serializers.HyperlinkedIdentityField(view_name='organization_detail')
  5. created = serializers.DateTimeField(read_only=True)
  6. name = serializers.CharField(
  7. max_length=100,
  8. validators=UniqueValidator(queryset=Organization.objects.all())
  9. )

UniqueTogetherValidator 应该应用到序列化器上,然后接受 queryset 参数和 fields 参数,这个参数应该是字段名的列表或元组。

  1. class RaceResultSerializer(serializers.Serializer):
  2. category = serializers.ChoiceField(['5k', '10k'])
  3. position = serializers.IntegerField()
  4. name = serializers.CharField(max_length=100)
  5. class Meta:
  6. validators = [UniqueTogetherValidator(
  7. queryset=RaceResult.objects.all(),
  8. fields=('category', 'position')
  9. )]

The UniqueForDateValidator classes.

REST framework 现在还包括用于验证 unique_for_dateunique_for_monthunique_for_year 模型字段约束的显式验证器类。这些在内部使用,而不是调用 Model.full_clean()

这些类被记录在文档的验证器部分中。


通用视图 (Generic views)

简化视图逻辑 (Simplification of view logic.)

由于新的序列化器 API,默认方法处理程序的视图逻辑已经大大简化。

Changes to pre/post save hooks.

pre_savepost_save 钩子不再存在,而是替换为 perform_create(self, serializer)perform_update(self, serializer)

这些方法应该通过调用 serializer.save() 来保存对象实例,并根据需要添加任何其他参数。他们还可以执行任何自定义预保存或后保存行为。

例如:

  1. def perform_create(self, serializer):
  2. # 直接包含 owner 属性,而不是来自请求数据。
  3. instance = serializer.save(owner=self.request.user)
  4. # 执行自定义后保存操作。
  5. send_email(instance.to_email, instance.message)

pre_deletepost_delete 钩子不再存在,取而代之的是 .perform_destroy(self, instance),它应该删除实例并执行任何自定义操作。

  1. def perform_destroy(self, instance):
  2. # 执行自定义预删除操作。
  3. send_deletion_alert(user=instance.created_by, deleted=instance)
  4. # 删除对象实例。
  5. instance.delete()

删除视图属性 (Removal of view attributes.)

视图实例上不再设置 .object.object_list 属性。将视图视为在视图处理期间存储状态的可变对象实例,这往往是糟糕的设计,并可能导致模糊流逻辑。

我个人建议开发人员将视图实例视为应用程序代码中的不可变对象。

PUT 为创建 (PUT as create.)

允许 PUT 作为创建操作是有问题的,因为它必须公开有关对象存在或不存在的信息。同样不明显的是,透明地允许重新创建先前删除的实例必然比简单地返回 404 响应具有更好的默认行为。

在不同的情况下,“PUT as 404” 和 “PUT as create” 两种样式都可以有效,但是我们现在选择了 404 行为作为默认设置,因为它更简单、更明显。

如果您需要恢复之前的行为,您可能需要将 AllowPUTAsCreateMixin作为 mixin 包含到您的视图中。

自定义错误响应 (Customizing error responses.)

通用视图现在为无效数据引发 ValidationFailed 异常。然后由异常处理程序处理此异常,而不是由视图直接返回 400 Bad Request 响应。

此更改意味着您现在可以轻松地在整个 API 中自定义错误响应的样式,而无需修改任何通用视图。


元数据 API (The metadata API)

处理 OPTIONS 请求的行为以前直接构建在基于类的视图中。现在已经正确地将其分离为元数据 API,该 API 允许与 REST framework 中的其他 API 策略具有相同的可插入样式。

这使得在整个 API 中为 OPTIONS 响应使用不同的样式更加容易,并使创建第三方元数据策略成为可能。


序列化器作为 HTML 表单 (Serializers as HTML forms)

REST framework 3.0 包含用于序列化器的模板化 HTML 表单渲染。

此 API 还没有最终确定,只会在 3.1 版本中推广到公共 API。

您需要注意的重大变化包括:

  • 现在支持嵌套的 HTML 表单,例如,当在可浏览的 API 中使用时,带有嵌套的 ProfileSerializerUserSerializer 将渲染嵌套 fieldset
  • 尚不支持嵌套的 HTML 表单列表,但计划用于 3.1。
  • 因为我们现在使用模板化 HTML 表单生成,所以 widget 选项不再适用于序列化器字段。您可以使用 style 字典控制用于给定字段的模板。

序列化器字段的 style 关键字参数 (The style keyword argument for serializer fields.)

style 关键字参数可用于将序列化器字段中的其他信息传递给渲染器类。特别是,HTMLFormRenderer 使用 base_template 键来确定用哪个模板渲染字段。

例如,要使用 textarea 控件而不是默认 input 控件,您将使用以下…

  1. additional_notes = serializers.CharField(
  2. style={'base_template': 'textarea.html'}
  3. )

同样,要使用单选按钮控件而不是默认 select 控件,您将使用以下…

  1. color_channel = serializers.ChoiceField(
  2. choices=['red', 'blue', 'green'],
  3. style={'base_template': 'radio.html'}
  4. )

这个 API 应该被认为是临时的,在即将发布的 3.1 版本中可能会有一些细微的变化。


API 样式 (API style)

我们在 API 响应中使用的默认样式有一些改进。

Unicode JSON by default.

Unicode JSON 现在是默认值。UnicodeJSONRenderer 类不再存在,并且已添加 UNICODE_JSON 设置。要还原此行为,请使用新设置:

  1. REST_FRAMEWORK = {
  2. 'UNICODE_JSON': False
  3. }

Compact JSON by default.

我们现在默认在响应中输出紧凑的 JSON。例如,我们返回:

  1. 我们现在默认在响应中输出紧凑的JSON。例如,我们返回:

而不是以下:

  1. {"email": "amy@example.com", "is_admin": true}

已添加 COMPACT_JSON 设置,如果需要,可用于还原此行为:

  1. REST_FRAMEWORK = {
  2. 'COMPACT_JSON': False
  3. }

文件字段为 URL (File fields as URLs)

FileFieldImageField 类现在默认表示为 URL。您应该确保正确设置 Django 的标准 MEDIA_URL 设置,并确保您的应用程序为上传的文件提供服务。

您可以使用 UPLOADED_FILES_USE_URL 设置键恢复此行为,并在表示中显示文件名:

  1. REST_FRAMEWORK = {
  2. 'UPLOADED_FILES_USE_URL': False
  3. }

您还可以使用 use_url 参数单独修改序列化器字段:

  1. uploaded_file = serializers.FileField(use_url=False)

还要注意,在实例化请求对象时,应该将请求对象作为上下文传递给序列化器,以便返回完全限定 URL。返回的 URL 将是 https://example.com/url_path/filename.txt 格式。例如:

  1. context = {'request': request}
  2. serializer = ExampleSerializer(instance, context=context)
  3. return Response(serializer.data)

如果从上下文中省略了请求,则返回的 URL 将采用 /url_path/filename.txt 格式。

使用 Retry-After 限制标头 (Throttle headers using Retry-After.)

自定义的 X-Throttle-Wait-Second 标头现在已经被放弃,取而代之的是标准的 Retry-After 标头。如果需要,可以通过为应用程序编写自定义异常处理程序来还原此行为。

日期和时间对象作为序列化器数据中的 ISO-8601 字符串 (Date and time objects as ISO-8601 strings in serializer data.)

在序列化器输出中,日期和时间对象现在默认强制为字符串。以前它们作为 DateTimeDateTime 对象返回,后来由渲染器强制转换为字符串。

您可以通过设置现有的 DATE_FORMATDATETIME_FORMATTIME_FORMAT 设置键来全局地修改此行为。将这些值设置为 None 而不是它们的默认值 'iso-8601' 将导致在序列化器数据中返回原生对象。

  1. REST_FRAMEWORK = {
  2. # 在 `serializer.data` 中返回原生 `Date` 和 `Time` 对象
  3. 'DATETIME_FORMAT': None
  4. 'DATE_FORMAT': None
  5. 'TIME_FORMAT': None
  6. }

您还可以使用 date_formattime_formatdatetime_format 参数单独修改序列化器字段:

  1. # 返回 `serializer.data` 中的 `DateTime` 实例,而不是字符串。
  2. created = serializers.DateTimeField(format=None)

Decimals as strings in serializer data.

在序列化器输出中,默认情况下小数被强制转换为字符串。以前它们作为 Decimal 对象返回,后来由渲染器强制转换为字符串。

您可以使用 COERCE_DECIMAL_TO_STRING 设置键全局修改此行为。

  1. REST_FRAMEWORK = {
  2. 'COERCE_DECIMAL_TO_STRING': False
  3. }

或者使用 coerce_to_string 关键字参数在单个序列化器字段上对其进行修改。

  1. # 返回 `serializer.data` 中的 `Decimal` 实例,而不是字符串。
  2. amount = serializers.DecimalField(
  3. max_digits=10,
  4. decimal_places=2,
  5. coerce_to_string=False
  6. )

默认的 JSON 渲染器将为未强制的 Decimal 实例返回浮点对象。这允许您根据 API 设计需要轻松切换小数的字符串或浮点表示。


杂项说明 (Miscellaneous notes)

  • 序列化器 ChoiceField 目前不显示嵌套选项,就像 2.4 所示那样。这将是 3.1 的一部分。
  • 由于新的模板化表单渲染,’widget’ 选项不再有效。这意味着使用第三方 “autocomplete” 小部件来渲染包含大量选项的选择输入并不是一种简单的方法。您需要使用常规选择或纯文本输入。如果有足够的需求,我们可以考虑在 3.1 或 3.2 中解决这个问题。
  • 某些默认验证错误消息已被重写,可能不再进行预翻译。如果您希望本地化它们,您仍然可以使用 Django 创建语言文件
  • APIException 子类以前可以在 detail 参数中使用任意类型。这些异常现在使用可翻译的文本字符串,因此在 detail 参数上作为结果调用 force_text,它必须是一个字符串。如果您需要 APIException 类的复杂参数,您应该将其子类化并覆盖 __init__() 方法。通常情况下,您需要使用自定义异常处理程序来提供非标准错误响应。

接下来会发生什么 (What’s coming next)

3.0 是一个增量版本,有几个即将推出的功能将建立在它所做的基线改进之上。

3.1 版本计划用于解决以下组件的改进:

  • 将序列化器用作 HTML 表单的公共 API。
  • 请求解析,媒体类型 & 可浏览 API 的实现。
  • 引入新的分页 API。
  • 更好地支持 API 版本控制。

3.2 版本计划为可浏览的 API 引入另一种管理样式的界面。

您可以跟踪 GitHub 站点上的开发,我们使用里程碑来指示计划时间表