【问题标题】:Django REST framework flat, read-write serializerDjango REST 框架扁平化、读写序列化器
【发布时间】:2013-08-21 13:18:34
【问题描述】:

在 Django REST 框架中,创建平面的读写序列化器表示涉及什么?文档引用了“平面表示”(http://django-rest-framework.org/api-guide/serializers.html#dealing-with-nested-objects 部分的结尾),但除了建议使用 RelatedField 子类之外,不提供示例或任何其他内容。

例如,如何在下面提供UserUserProfile 关系的平面表示?

# Model
class UserProfile(models.Model):
    user = models.OneToOneField(User)
    favourite_number = models.IntegerField()

# Serializer
class UserProfileSerializer(serializers.ModelSerializer):
    email = serialisers.EmailField(source='user.email')
    class Meta:
        model = UserProfile
        fields = ['id', 'favourite_number', 'email',]

上述UserProfileSerializer 不允许写入email 字段,但我希望它充分表达了意图。那么,应该如何构造一个“平面”读写序列化程序以允许UserProfileSerializer 上的可写email 属性?子类化 ModelSerializer 时是否有可能做到这一点?

谢谢。

【问题讨论】:

标签: serialization django-rest-framework


【解决方案1】:

查看 Django REST 框架 (DRF) 源代码后,我认为 DRF 序列化程序与随附的模型紧密相关,以实现反序列化目的。 Fieldsource 参数使这不那么用于序列化目的。

考虑到这一点,并将序列化程序视为封装验证和保存行为(除了它们的(非)序列化行为),我使用了两个序列化程序:一个用于 User 和 UserProfile 模型:

class UserSerializer(serializer.ModelSerializer):
    class Meta:
        model = User
        fields = ['email',]

class UserProfileSerializer(serializer.ModelSerializer):
    email = serializers.EmailField(source='user.email')
    class Meta:
        model = UserProfile
        fields = ['id', 'favourite_number', 'email',]

EmailField 上的 source 参数可以充分处理序列化情况(例如,在处理 GET 请求时)。对于反序列化(例如在处理 PUT 请求时),有必要在视图中做一些工作,结合两个序列化程序的验证和保存行为:

class UserProfileRetrieveUpdate(generics.GenericAPIView):
    def get(self, request, *args, **kwargs):
        # Only UserProfileSerializer is required to serialize data since
        # email is populated by the 'source' param on EmailField.
        serializer = UserProfileSerializer(
                instance=request.user.get_profile())
        return Response(serializer.data)

    def put(self, request, *args, **kwargs):
        # Both UserProfileSerializer and UserProfileSerializer are required
        # in order to validate and save data on their associated models.
        user_profile_serializer = UserProfileSerializer(
                instance=request.user.get_profile(),
                data=request.DATA)
        user_serializer = UserSerializer(
                instance=request.user,
                data=request.DATA)
        if user_profile_serializer.is_valid() and user_serializer.is_valid():
            user_profile_serializer.save()
            user_serializer.save()
            return Response(
                    user_profile_serializer.data, status=status.HTTP_200_OK)
        # Combine errors from both serializers.
        errors = dict()
        errors.update(user_profile_serializer.errors)
        errors.update(user_serializer.errors)
        return Response(errors, status=status.HTTP_400_BAD_REQUEST)

【讨论】:

  • Paul,您的 request.DATA 是单个 JSON 嵌套数组,还是 POST 请求中的每个模型都有一个? (我正在尝试实现类似的目标)
  • @jvc26,我上面的示例将使用request.DATA 中的单个根 JSON 对象。 request.DATA 看起来像这样:{ 'id': '1', 'favourite_number': '2', 'email': 'me@example.com' } 就客户端而言,该 JSON 对象表示单个模型实例,并且不知道它实际上的两个模型(UserUserProfile)分解到服务器上。 UserSerializerUserProfileSerializer 用于从 request.DATA 中提取、验证和保存相关模型的内容。
【解决方案2】:

首先:更好地处理嵌套写入。

第二:Serializer Relations docsPrimaryKeyRelatedFieldSlugRelatedField 都说“默认情况下,此字段是可读写的......” - 所以如果您的电子邮件字段是唯一的(是吗?)它可能是您可以使用SlugRelatedField,它就可以工作——我还没有尝试过(但是)。

第三:相反,我使用了一个普通的Field 子类,它使用source="*" technique 来接受整个对象。从那里我手动拉出to_native 中的相关字段并返回——这是只读的。为了写作,我检查了post_save 中的 request.DATA 并更新了那里的相关对象——这不是自动的,但它可以工作。

所以,第四:看看你已经得到了什么,我的方法(上面)相当于将你的 email 字段标记为只读,然后实现 post_save 以检查 email 值并执行相应地更新。

【讨论】:

  • 谢谢,@carton-gibson。 (1) 我期待着更容易编写嵌套表示。 (2) 我的例子是为了说明而简化的,但你的建议很有趣。 (3 和 4)我想到了一些 post_save() 摆弄,我在其他情况下也使用过,但是使用两个序列化程序(我自己的答案)似乎更干净、更健壮。
【解决方案3】:

虽然这并不能严格回答问题 - 我认为它会解决您的需求。问题可能更多在于将两个模型拆分为一个实体,而不是 DRF 的问题。

从 Django 1.5 开始,您可以创建自定义用户,如果您想要的只是一些方法和额外的字段,但除此之外您对 Django 用户感到满意,那么您需要做的就是:

class MyUser(AbstractBaseUser): favourite_number = models.IntegerField()

在设置中:AUTH_USER_MODEL = 'myapp.myuser'

(当然还有一个 db-migration,可以通过使用 db_table 选项指向您现有的用户表并在那里添加新列来变得非常简单)。

之后,你就有了 DRF 擅长的常见情况。

【讨论】:

    猜你喜欢
    • 2013-07-20
    • 2015-06-29
    • 2020-03-22
    • 2015-10-29
    • 2020-05-08
    • 2015-05-06
    • 2018-12-13
    • 2016-03-31
    • 1970-01-01
    相关资源
    最近更新 更多