14

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

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

# 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字段,但我希望它充分表达了意图。那么,应该如何构造一个“平面”读写序列化程序以允许emailUserProfileSerializer? 子类化 ModelSerializer 时是否有可能做到这一点?

谢谢。

4

3 回答 3

8

查看 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',]

上的source参数EmailField充分处理序列化情况(例如,在服务 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)
于 2013-08-20T11:51:02.223 回答
2

第一:对嵌套写入的更好处理正在进行中。

第二:序列化器关系文档说“默认情况下这个字段是读写的......” - 所以如果你的电子邮件字段是唯一的(是吗?)它可能是你可以使用它并且它会工作 -我还没有尝试过(但是)。PrimaryKeyRelatedFieldSlugRelatedFieldSlugRelatedField

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

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

于 2013-08-20T09:42:02.043 回答
0

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

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

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

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

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

之后,您就有了 DRF 擅长的常见案例。

于 2014-04-22T13:25:26.117 回答