2016-03-23 28 views
2

我不完全確定我的問題的標題是否與我想要的一樣具體,但情況如此:Django Rest Framework接收POST中的主鍵值並將模型對象作爲嵌套序列化器

我有一個HyperlinkedModelSerializer,看起來像這樣:

class ParentArrivalSerializer(serializers.HyperlinkedModelSerializer): 
    carpool = SchoolBuildingCarpoolSerializer() 

    class Meta: 
     model = ParentArrival 

正如你可以看到carpool被定義爲一個嵌套的序列化對象,我要的是能夠使一個POST請求,以這種方式來創建一個ParentArrival (data as application/json):

{ 
    ... 
    "carpool": "http://localhost:8000/api/school-building-carpools/10/" 
    ... 
} 

並以這種方式得到的數據:

{ 
    "carpool": { 
     "url": "http://localhost:8000/api/school-building-carpools/10/" 
     "name": "Name of the carpool", 
     ... 
    } 
} 

基本上,我正在尋找一種方式來處理嵌套串行無需發送數據作爲一個對象(但ID或網址在這種情況下, )在POST請求中,但接收嵌套在序列化響應中的對象。

回答

6

我一直很滿意我以前的解決方案,但決定再看一遍,我想我有另一種解決方案,完全符合你的要求。

基本上,你需要創建自己的自定義字段,只是覆蓋to_representation方法:

class CarpoolField(serializers.PrimaryKeyRelatedField): 
    def to_representation(self, value): 
     pk = super(CarpoolField, self).to_representation(value) 
     try: 
      item = ParentArrival.objects.get(pk=pk) 
      serializer = CarpoolSerializer(item) 
      return serializer.data 
     except ParentArrival.DoesNotExist: 
      return None 

    def get_choices(self, cutoff=None): 
     queryset = self.get_queryset() 
     if queryset is None: 
      return {} 

     return OrderedDict([(item.id, str(item)) for item in queryset]) 

class ParentArrivalSerializer(serializers.HyperlinkedModelSerializer): 
    carpool = CarpoolField(queryset=Carpool.objects.all()) 

    class Meta: 
     model = ParentArrival 

這將允許您發佈與

{ 
    "carpool": 10 
} 

,並得到:

{ 
    "carpool": { 
     "url": "http://localhost:8000/api/school-building-carpools/10/" 
     "name": "Name of the carpool", 
     ... 
    } 
} 
+0

它就像一個魅力!我不得不做一些其他的黑客,因爲我的API是超鏈接的,但你的解決方案伎倆! –

+0

我們可以創建一個像這樣的通用字段,我們可以在初始化時傳遞串行器嗎? – Anuj

+0

很酷的解決方案。但值得注意的是,這意味着您只能發佈主鍵而不是豐富的對象。這在API場景中令人沮喪,因爲它意味着你可以從你的django應用程序獲取資源(角度:'$ http.get('/ my/resource');',但是不能保存該對象而不更改嵌入對象返回int。 – chukkwagon

2

做到這一點的一種方法是將'carpool'作爲從DRF獲得的默認值,然後爲嵌套對象添加一個只讀字段。

像這樣的東西(我沒有時間來測試代碼,所以認爲這僞代碼如果你不能得到它的工作,讓我知道了,會花更多的時間。):

class ParentArrivalSerializer(serializers.HyperlinkedModelSerializer): 
    carpool_info = serializers.SerializerMethodField(read_only=True) 

    class Meta: 
     model = ParentArrival 
     fields = ('id', 'carpool', 'carpool_info',) 

    def get_carpool_info(self, obj): 
     carpool = obj.carpool 
     serializer = SchoolBuildingCarpoolSerializer(carpool) 
     return serializer.data 

如果您唯一的嵌套對象是拼車,我還建議切換到常規的ModelSerializer,以便拼車只顯示ID(10),然後嵌套的對象可以顯示URL。

class ParentArrivalSerializer(serializers.ModelSerializer): 
    .... 

,然後,如果所有的工作,你就可以做了一條信息

{ 
    "carpool": 10 
} 

和你可以獲得:

{ 
    "carpool": 10 
    "carpool_info": { 
     "url": "http://localhost:8000/api/school-building-carpools/10/" 
     "name": "Name of the carpool", 
     ... 
    } 
} 

我從來沒有發現另一種解決方案,所以這是我多次使用的技巧。

+0

我忘了提,我已經知道這個解決方案(目前我使用它在這個項目上),但我正在尋找一個更「優雅」的方式來做到這一點,而不必把一個額外的每個嵌套的read_only字段。 –

+0

我不得不使用這個解決方案。自定義Field類的「更聰明」的方式比我能算的更多。 –

0

重寫to_representation方法怎麼樣?

class YourSerializer(serializers.ModelSerializer): 

    class Meta: 
     model = ModelClass 
     fields = ["id", "foreignkey"] 

    def to_representation(self, instance): 
     data = super(YourSerializer, self).to_representation(instance) 
     data['foreignkey'] = YourNestedSerializer(instance.foreignkey).data 
     return data 
+0

我認爲這是一種優雅的方式,你不必創造自己的領域,因爲你不需要添加像'foreignkey_info'這樣的新字段,這對於前端用戶來說可能更方便ENCA爲此數據結構調整對象。 – ramwin

相關問題