Django Rest Framework 使 OnetoOne 关系感觉就像是一个模型

Posted

技术标签:

【中文标题】Django Rest Framework 使 OnetoOne 关系感觉就像是一个模型【英文标题】:Django Rest Framework make OnetoOne relation ship feel like it is one model 【发布时间】:2013-09-02 06:29:02 【问题描述】:

我的User 保存在两个不同的模型中,UserProfileUser。现在从 API 的角度来看,没有人真正关心这两者是否不同。

所以我有:

class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('url', 'username', 'first_name', 'last_name', 'email')

class UserPSerializer(serializers.HyperlinkedModelSerializer):
    full_name = Field(source='full_name')
    class Meta:
        model = UserProfile
        fields = ('url', 'mobile', 'user','favourite_locations')

所以在UserPSerializer 中,user 字段只是指向该资源的链接。但是从用户的角度来看,他根本没有理由知道User

是否有一些技巧可以让我将它们混合在一起并将它们作为一个模型呈现给用户,或者我必须以某种方式手动执行此操作。

【问题讨论】:

查看[此相关问题的答案][1]。我认为它涵盖了你需要的东西。 [1]:***.com/questions/18012665/… 这个答案也很有用:***.com/a/19806796/2789332 如果您喜欢下面的答案,请记得接受! 【参考方案1】:

如果您还覆盖序列化程序上的创建和更新方法,则可以使用 @kahlo's approach 进行 POST 和 PUT。

给定这样的配置文件模型:

class Profile(models.Model):
    user = models.OneToOneField(User)
    avatar_url = models.URLField(default='', blank=True)  # e.g.

这是一个读取和写入附加配置文件字段的用户序列化程序:

class UserSerializer(serializers.HyperlinkedModelSerializer):
    # A field from the user's profile:
    avatar_url = serializers.URLField(source='profile.avatar_url', allow_blank=True)

    class Meta:
        model = User
        fields = ('url', 'username', 'avatar_url')

    def create(self, validated_data):
        profile_data = validated_data.pop('profile', None)
        user = super(UserSerializer, self).create(validated_data)
        self.update_or_create_profile(user, profile_data)
        return user

    def update(self, instance, validated_data):
        profile_data = validated_data.pop('profile', None)
        self.update_or_create_profile(instance, profile_data)
        return super(UserSerializer, self).update(instance, validated_data)

    def update_or_create_profile(self, user, profile_data):
        # This always creates a Profile if the User is missing one;
        # change the logic here if that's not right for your app
        Profile.objects.update_or_create(user=user, defaults=profile_data)

生成的 API 根据需要呈现平面用户资源:

GET /users/5/

    "url": "http://localhost:9090/users/5/", 
    "username": "test", 
    "avatar_url": "http://example.com/avatar.jpg"

您可以在 POST 和 PUT 请求中包含配置文件的 avatar_url 字段。 (并且用户资源上的 DELETE 也会删除其 Profile 模型,尽管这只是 Django 的正常删除级联。)

这里的逻辑将总是为用户创建一个 Profile 模型,如果它丢失了(在任何更新中)。对于用户和个人资料,这可能就是您想要的。对于其他关系,它可能不是,您需要更改更新或创建逻辑。 (这就是为什么 DRF doesn't automatically write through a nested relationship 适合你。)

【讨论】:

【参考方案2】:

我刚遇到这个;我还没有找到一个好的解决方案,特别是回信给我的UserUserProfile 模型。我目前正在使用 SerializerMethodField 手动扁平化我的序列化程序,这非常令人恼火,但它确实有效:

class UserSerializer(serializers.HyperlinkedModelSerializer):

    mobile = serializers.SerializerMethodField('get_mobile')
    favourite_locations = serializers.SerializerMethodField('get_favourite_locations')

    class Meta:
        model = User
        fields = ('url', 'username', 'first_name', 'last_name', 'email', 'mobile', 'favourite_locations')

    def get_mobile(self, obj):
        return obj.get_profile().mobile

    def get_favourite_locations(self, obj):
        return obj.get_profile().favourite_locations

这是非常糟糕的手动操作,但您最终会得到:


    "url": "http://example.com/api/users/1",
    "username": "jondoe",
    "first_name": "Jon",
    "last_name": "Doe",
    "email": "jdoe@example.com",
    "mobile": "701-680-3095",
    "favourite_locations": [
        "Paris",
        "London",
        "Tokyo"
    ]

我猜这就是你要找的。​​p>

【讨论】:

我认为 SerializerMethodField 是只读的,因此这不允许通过用户资源创建或修改配置文件字段。您还可以使用@kahlo 的答案中的建议,通过少输入 little 获得只读。 (例如,mobile = serializers.CharField(source='profile.mobile', read_only=True))。 mobile = serializers.SerializerMethodField() 默认使用函数get_mobile,如果您手动指定字段,则会抛出冗余错误。【参考方案3】:

我会在 UserPSerializer 上实施修改,因为字段不会增长:

class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('url', 'username', 'first_name', 'last_name', 'email')

class UserPSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.CharField(source='user.url')
    username = serializers.CharField(source='user.username')
    first_name = serializers.CharField(source='user.first_name')
    last_name = serializers.CharField(source='user.last_name')
    email = serializers.CharField(source='user.email')

    class Meta:
        model = UserProfile
        fields = ('mobile', 'favourite_locations',
                  'url', 'username', 'first_name', 'last_name', 'email')

【讨论】:

这是获取用户的干净解决方案,但不适用于发布新用户。

以上是关于Django Rest Framework 使 OnetoOne 关系感觉就像是一个模型的主要内容,如果未能解决你的问题,请参考以下文章

Django rest framework JWT ,删除 jwt 令牌

django-rest-framework - 在可浏览的 API 中自动生成表单?

Django后端开发学习笔记Django REST Framework基于类的视图

Django后端开发学习笔记Django REST Framework基于类的视图

POST 请求创建多个对象 AJAX/Django Rest Framework

Django Rest Framework 序列化程序中的循环依赖