2013-08-29 93 views
33

我有我的User保存在兩個不同的模型,UserProfileUser。從API的角度來看,沒有人真正關心這兩個不同。Django Rest Framework使OnetoOne關係船感覺像是一個模型

所以我在這裏有:

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

是否有一些技巧,我可以將它們混合在一起,並將它們作爲一個模型呈現給用戶,或者我必須以某種方式手動執行此操作。

+0

查看本文[相關問題的回答] [1]。我認爲它涵蓋了你需要的東西。 [1]:http:// stackoverflow。com/questions/18012665/how-can-one-customize-django-rest-framework-serializers-output/ –

+2

這個答案也很有用:http://stackoverflow.com/a/19806796/2789332 – Avril

+0

記得接受,如果你就像下面的答案一樣! – bbengfort

回答

12

我剛碰到這個;我還沒有找到一個很好的解決方案,特別是寫回我的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": "[email protected]", 
    "mobile": "701-680-3095", 
    "favourite_locations": [ 
     "Paris", 
     "London", 
     "Tokyo" 
    ] 
} 

其中,我猜是你在找什麼。

+2

我認爲SerializerMethodField是隻讀的,所以這不允許通過用戶資源創建或修改配置文件字段。您也可以使用@ kahlo的答案中的建議,以少量少打字的方式閱讀。 (例如'mobile = serializers.CharField(source ='profile.mobile',read_only = True)')。 – medmunds

+0

'mobile = serializers.SerializerMethodField()'默認會使用'get_mobile'函數,如果您手動指定該字段,將會引發冗餘錯誤。 – andyn

6

我會實現對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') 
+4

這是一個乾淨利索的解決方案,但不適用於發佈新用戶。 – Xuan

+1

只是重寫更新並創建.. –

29

您可以發佈與@kahlo's approach PUT,如果你還覆蓋創建並在您的串行更新方法。

鑑於輪廓模型是這樣的:

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

這裏有一個用戶序列化程序讀取和寫入附加狀態字段(S):

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請求中的字段。 (和刪除用戶的資源也將刪除其剖面模型,雖然這只是Django的正常刪除級聯。)

這裏的邏輯將總是爲用戶創建一個配置文件模式,如果它的缺失(任何更新)。用戶和配置文件,這可能是你想要的。對於其他關係可能不是,您需要更改更新或創建邏輯。 (這就是爲什麼DRF爲doesn't automatically write through a nested relationship

相關問題