2015-10-29 63 views
6

我是python和django休息的新手。但我很困惑。在django rest框架中更新多對多關係的最佳方式是什麼? 我閱讀文檔 http://www.django-rest-framework.org/api-guide/relations/#manytomanyfields-with-a-through-model 默認情況下,將具有指定的貫穿模型的ManyToManyField作爲目標的關係字段設置爲只讀。Django Rest更新多對多ID

如果您明確指定一個指向具有直通模型的ManyToManyField的關係字段,請確保將read_only設置爲True。

所以,如果我有一個代碼

class Master(models.Model): 
    # other fields 
    skills = models.ManyToManyField(Skill) 

class MasterSerializer(serializers.ModelSerializer): 
    skills = SkillSerializer(many=True, read_only=False) 

這將返回技能對象列表。我沒有辦法更新它們。據我所知,當談到M2M時,Django更喜歡使用對象和對象ID。如果我使用yii或rails,我將使用「through」模型。我想獲得skill_ids字段。我可以讀寫。我可以這樣做寫操作

class MasterSerializer(serializers.ModelSerializer): 
    skill_ids = serializers.ListField(write_only=True) 

    def update(self, instance, validated_data): 

    # ... 
    validated_data['skill_ids'] = filter(None, validated_data['skill_ids']) 
    for skill_id in validated_data['skill_ids']: 
     skill = Skill.objects.get(pk=skill_id) 
     instance.skills.add(skill) 

    return instance 

但我不能讓它返回字段中的skill_ids。併爲讀寫操作工作。

+0

目前還不清楚什麼你正在嘗試做的。你可以添加一個你想插入的JSON例子嗎? – Linovia

+0

{ID:123,FIRST_NAME: 「約翰」, 「skill_ids」:1,2,3]} – radzserg

+0

常見的場景 - 你的技能列表和你的ID不是對象進行操作。發送完整對象來更新關係是沒有意義的。 { 「FIRST_NAME」: 「約翰」, 「技巧」:[ {ID:1,名稱: 「名稱1」}, {ID:2,名稱: 「名稱2」}, ] } – radzserg

回答

11

有幾件事要注意。

首先,你不要在你的例子中通過表格。因此您可以跳過該部分。

其次是你正試圖使用​​嵌套序列化器,這比你試圖實現的複雜得多。

你可以簡單的讀/寫相關ID使用PrimaryKeyRelatedField:

class MasterSerializer(serializers.ModelSerializer): 
    skills_ids = serializers.PrimaryKeyRelatedField(many=True, read_only=False, queryset=Skill.objects.all(), source='skills') 

這應該是能讀/寫:

{id: 123, first_name: "John", "skill_ids": [1, 2, 3]} 

注意,從JSON的 「skill_ids」 的映射模型的「技能」通過使用可選參數源完成

+0

謝謝,這是我需要的。 – radzserg

+0

如果設計指出:「選擇現有的或添加新的」。我們應該總是使用嵌套序列化器嗎? – Feanor

+0

在這種情況下,您可以先創建相關實例,然後將其添加到列表中或使用嵌套序列化器獲取。 – Linovia

1

我會嘗試在設計方面帶來一些亮點:在Django中,如果您爲ManyToManyRelation指定模型,那麼模型上的關係字段將變爲只讀。如果您需要更改直接在直通模型上執行的關聯,請刪除或註冊新記錄。

這意味着您可能需要爲直通模型使用完全不同的序列化程序,或者編寫自定義更新/創建方法。

有一些設置返回自定義通過模型,你確定你不夠好的默認實現ManyToManyFields嗎?

+0

那麼我對django-rest的實現感興趣。而現在我看不到一種方式,我怎麼能用內置工具更新m2m關係。 – radzserg

0

我已經處理了這個問題很長一段時間,我發現解決一般問題的最佳方法是更新任何很多領域的方法是通過解決它。

在我的情況下,有一個名爲Listing的模型,用戶可以將一個Subscription(另一個模型)作爲Listing模型的一個實例。訂閱使用通用外鍵,列表通過Many2Many導入用戶的訂閱。

而是通過API進行PUT請求上市型號的,我只是認購實例添加到認購的API觀的POST方法正確的模式。這裏是我的調整代碼:

#Model 
    class Listing(models.Model): 

     #Basics 
     user = models.ForeignKey(settings.AUTH_USER_MODEL) 
     slug = models.SlugField(unique=True, blank=True) 
     timestamp = models.DateTimeField(auto_now_add=True, auto_now=False) 

     #Listing 
     title = models.CharField(max_length=200) 
     price = models.CharField(max_length=50, null=True, blank=True) 
     subscriptions = models.ManyToManyField(Subscription, blank=True) 

    class Subscription(models.Model): 
     user = models.ForeignKey(settings.AUTH_USER_MODEL) 
     content_type = models.ForeignKey(ContentType) 
     object_id = models.PositiveIntegerField() 
     content_object = GenericForeignKey('content_type', 'object_id') 
     timestamp = models.DateTimeField(auto_now_add=True) 

    #Views 
    class APISubscriptionCreateView(APIView): #Retrieve Detail 

     def post(self, request, format=None): 
     serializer = SubscriptionCreateSerializer(data=request.data) 
     if serializer.is_valid(): 
      sub = serializer.save(user=self.request.user) 
      object_id = request.data['object_id'] 
      lis = Listing.objects.get(pk=object_id) 
      lis.subscriptions.add(sub) 

      return Response(serializer.data, status=status.HTTP_201_CREATED) 
     return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 

我希望這會有所幫助,我花了一段時間來算出這個