2016-12-20 91 views
1

我無法獲得DRF以允許在一個序列化器中提供read_only版本的嵌套數據和只讀ID列表中的可寫入版本。這對我來說就像是一個bug,但通常這隻意味着我不能很好地理解框架,並被錯誤信息誤導。Django Rest Framework對嵌套數據中的read_only沒有響應

class Individual(models.Model): 
    household = models.ForeignKey(
     'household.Household', 
     null=True, 
     related_name="individuals") 
    name = models.CharField(
     max_length=100, default='') 

class Household(models.Model): 
    address_line1 = models.CharField(max_length=64, default='') 

class IndividualListSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = Individual 
     depth = 0 
     fields = ('url', 'id', 'name', 'household') 
     read_only_fields = fields 

class HouseholdUpdateSerializer(serializers.ModelSerializer): 
    individuals_details = IndividualListSerializer(many=True, source='individuals', read_only=True) 
    class Meta: 
     model = Household 
     fields = ('id', 'address_line1', 'individuals', 'individuals_details') 
     read_only_fields = ('id', 'individuals_details') 

誤差回來爲

AssertionError: The `.update()` method does not support writable nested fields by default. Write an explicit `.update()` method for serializer `household.serializers.HouseholdUpdateSerializer`, or set `read_only=True` on nested serializer fields. // Werkzeug Debugger</title> 

我已經在嵌套字段(這是需要在響應於更新)READ_ONLY使用。然而,這個錯誤仍然表明我沒有這樣做。

如果我完全刪除individuals字段,則individuals_details會返回可讀的數據而不會出現錯誤,但由於它忽略了要發送的數據,因此它不會更新該列表。

如果我刪除individuals_details字段,則DRF接受individuals列表並在模型上執行更新。但是那時所需的回報數據並不存在。

所以,當讀取嵌套或寫入列表自己工作時,當添加另一個時,序列化程序不起作用。

這似乎是一個相當普遍的地方人們卡住了,而且似乎答案this SO question已成爲模式的最佳做法。但由於某種原因,它在我的代碼中不起作用。也許是因爲我的模型中有ManyToOne。

我可以通過改變客戶端來執行PUT更新,忽略響應,然後做一個單獨的GET,但這是sl and不馴,如果DRF更新可以做到按預期工作。

我在這裏遺漏了什麼?

+0

我不確定'read_only_fields'是否適用於顯式定義的字段。如何從'read_only_fields'中刪除個人詳細信息?它修復了嗎? – Linovia

+0

另請注意,這是一種反模式,可將關係字段和嵌套序列化程序都用於同一模型。 – Linovia

+0

這不是我的目標,使用關係和嵌套。但是DRF不允許我更新嵌套數據的ID。那麼,在更改哪些ID相關後,獲取有關子記錄的詳細信息的好方法是什麼? – shanemgrey

回答

1

您提出了幾個問題,所以我將從表示開始。其實你不需要單獨的字段完整Individual視圖你可以只用ovveride to_representation方法。

class HouseholdUpdateSerializer(serializers.ModelSerializer): 

    class Meta: 
     model = Household 
     fields = ('id', 'address_line1', 'individuals') 

    def to_representation(self, instance): 
     representation = super(HouseholdUpdateSerializer, self).to_representation(instance) 
     representation['individuals'] = IndividualListSerializer(instance.individuals.all(), many=True).data 
     return representation 

通常當你希望你的模型更新相關的領域,你應該重寫createupdate串行方法。

class HouseholdUpdateSerializer(serializers.ModelSerializer): 
    .... 
    def update(instance, validated_data): 
     # get and remove individuals from validated_data 
     individuals = validated_data.pop('individuals') 
     # delete all related links to individuals 
     # You could provide some validation before clear, check if provided pks exists in db table 
     instance.individuals.clear() 
     # update related links with new individuals 
     instance.individuals.add(*individuals) 
     # call super to provide update for other fields 
     return super(HouseholdUpdateSerializer, self).update(validated_data) 

create可能會在您的情況下很好地工作,而不會重寫。如果不是隻寫它類似於update

+0

覆蓋to_representation爲我所需要的工作。我不清楚重寫更新方法的第二部分在做什麼。我的猜測是我還需要爲個人定製驗證器? – shanemgrey

+0

@shanemgrey確定你可以在清除它們之間的鏈接之前檢查「個人」密鑰是否存在於db中。如果某個鍵錯了,你會拋出一個異常,然後用'if serializer將其捕獲到視圖層。is_valid():..'表達式或'serializer.save(raise_exception = True)'。你可以使用'queryset.exists()'爲每個密鑰驗證你的密鑰,或者檢查你的'len',你鍵入和計算db中的密鑰,就像這樣'if len(individual)== Individuals.objects.filter(pk__in =個體).Count之間()'。 –

相關問題