2012-07-19 51 views
6

我得到,如果運行下面的代碼超過最大遞歸深度:當反向關係中full = True時,Django Tastypie會拋出'超出最大遞歸深度'。

from tastypie import fields, utils 
from tastypie.resources import ModelResource 
from core.models import Project, Client 


class ClientResource(ModelResource): 
    projects = fields.ToManyField(
     'api.resources.ProjectResource', 'project_set', full=True 
    ) 
    class Meta: 
     queryset = Client.objects.all() 
     resource_name = 'client' 


class ProjectResource(ModelResource): 
    client = fields.ForeignKey(ClientResource, 'client', full=True) 
    class Meta: 
     queryset = Project.objects.all() 
     resource_name = 'project' 

# curl http://localhost:8000/api/client/?format=json 
# or 
# curl http://localhost:8000/api/project/?format=json 

如果一套全=假在它工作的關係之一。我明白爲什麼會發生這種情況,但我需要兩種關係才能提供數據,而不僅僅是「resource_uri」。是否有Tastypie的方式來做到這一點?我設法解決了在我的項目模型中創建序列化方法的問題,但它遠非優雅。謝謝。

回答

13

您必須在至少一個資源上覆蓋full_dehydrate方法以跳過導致遞歸的相關資源脫水。

或者,您可以定義兩種類型的資源,使用full=Truefull=False中的一個模型。

+3

感謝兩個資源尖,幫我出了不少...... :) – 2012-09-14 00:06:00

+0

@MarkShust:我面臨着類似的問題,可以請更多地討論任何的爲你工作的方法? – 2015-07-16 15:03:08

+0

@NikhilAgrawal如果我沒有記錯,我只是提出了兩個資源,一個是full = True,一個是full = False,後面是full = False,後面跟着簡單,所以我知道哪個資源是什麼。然後參考適當的資源。 – 2015-07-16 18:39:32

3

謝謝@astevanovic指向正確的方向。

我發現重寫dehydrate方法來處理只有一些指定的字段比重寫full_hydrate方法略過繁瑣,以略過字段。

爲了追求可重用性,我想出了以下代碼片段。希望這將是有用的一些:

class BeeModelResource(ModelResource): 

    def dehydrate(self, bundle): 
     bundle = super(BeeModelResource, self).dehydrate(bundle) 
     bundle = self.dehydrate_partial(bundle)   
     return bundle 

    def dehydrate_partial(self, bundle): 
     for field_name, resource_field in self.fields.items(): 
      if not isinstance(resource_field, RelatedField): 
       continue 

      if resource_field.full: # already dehydrated 
       continue 

      if not field_name in self._meta.partial_fields: 
       continue 

      if isinstance(resource_field, ToOneField): 
       fk_object = getattr(bundle.obj, resource_field.attribute) 
       fk_bundle = Bundle(obj=fk_object, request=bundle.request) 
       fk_resource = resource_field.get_related_resource(fk_object) 

       bundle.data[field_name] = fk_resource.dehydrate_selected( 
         fk_bundle, self._meta.partial_fields[field_name]).data 
      elif isinstance(resource_field, ToManyField): 
       data = [] 

       fk_objects = getattr(bundle.obj, resource_field.attribute) 
       for fk_object in fk_objects.all(): 
        fk_bundle = Bundle(obj=fk_object, request=bundle.request) 
        fk_resource = resource_field.get_related_resource(fk_object) 
        fk_bundle = fk_resource.dehydrate_selected_fields( 
          fk_bundle, self._meta.partial_fields[field_name]) 
        data.append(fk_bundle.data) 
       bundle.data[field_name] = data 

     return bundle 

    def dehydrate_selected_fields(self, bundle, selected_field_names): 
     # Dehydrate each field. 
     for field_name, field_object in self.fields.items(): 
      # A touch leaky but it makes URI resolution work. 
      # (borrowed from tastypie.resources.full_dehydrate) 
      if field_name in selected_field_names and not self.is_special_fields(field_name): 
       if getattr(field_object, 'dehydrated_type', None) == 'related': 
        field_object.api_name = self._meta.api_name 
        field_object.resource_name = self._meta.resource_name 

       bundle.data[field_name] = field_object.dehydrate(bundle) 

     bundle.data['resource_uri'] = self.get_resource_uri(bundle.obj) 
     bundle.data['id'] = bundle.obj.pk 

     return bundle 

    @staticmethod 
    def is_special_fields(field_name): 
     return field_name in ['resource_uri'] 

隨着@sigmus'例如,資源將需要3次修改:

  1. 資源都將使用BeeModuleResource作爲它的超類(或加dehydrate_partial一個資源和dehydrate_selected其他。)
  2. 在任一資源未設置full=True
  3. 添加partial_fields到資源Meta未凝固的資源

```

class ClientResource(BeeModelResource): # make BeeModelResource a super class 
    projects = fields.ToManyField(
     'api.resources.ProjectResource', 'project_set' 
    ) # remove full=True 
    class Meta: 
     queryset = Client.objects.all() 
     resource_name = 'client' 
     partial_fields = {'projects': ['memo', 'title']} # add partial_fields 

class ProjectResource(BeeModelResource): # make BeeModelResource a super class 
    client = fields.ForeignKey(ClientResource, 'client', full=True) 
    class Meta: 
     queryset = Project.objects.all() 
     resource_name = 'project' 
相關問題