2012-07-19 6 views
6

uzyskać maksymalną głębokość rekurencji przekroczony jeśli uruchomić kod poniżej:Django Tastypie podaje "maksymalną przekroczoną głębokość rekursji", gdy jest pełna = Prawdziwa zależność odwrotna.

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 

Jeśli zestaw pełny = False na jednej z relacji to działa. Rozumiem, dlaczego tak się dzieje, ale potrzebuję obu relacji, by przynieść dane, a nie tylko "resource_uri". Czy istnieje sposób Tastypie to zrobić? Udało mi się rozwiązać problem, tworząc metodę serializacji na moim Modelu Projektu, ale nie jest ona elegancka. Dzięki.

Odpowiedz

13

Musisz zastąpić metodę full_dehydrate na co najmniej jednym zasobie, aby pominąć związany z odwadnianiem zasób, który powoduje rekursję.

Alternatywnie można zdefiniować dwa typy zasobów, które używają tego samego modelu, jeden z full=True, a drugi z full=False.

+3

Dzięki za cynk dwóch zasobów, to pomogło mi się dużo ... :) –

+0

@MarkShust: Jestem stoi podobny rodzaj problemu, można proszę opracować bardziej na którykolwiek z metoda, która działała dla ciebie? –

+0

@NikhilAgrawal jeśli dobrze pamiętam, zrobiłem dwa zasoby, jeden z full = True, jeden z full = False, sufiks full = False with Simple, więc wiem, który zasób jest czym. następnie odwołaj się do odpowiedniego zasobu. –

3

Dzięki @astevanovic wskazuje właściwy kierunek.

Zauważyłem, że przesłonięcie metody dehydrate przetwarzania tylko niektórych określonych pól jest nieco mniej żmudne niż przesłonięcie metody full_hydrate, aby pominąć pola.

W pogoni za możliwością ponownego użycia, wymyśliłem następujące fragmenty kodu. Nadzieję, że dobrze byłoby dla niektórych:

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'] 

Z przykład @sigmus', zasoby będą potrzebne 3 zmiany:

  1. zarówno zasobów użyje BeeModuleResource jako super klasy (lub dodać do jednej dehydrate_partial zasobów i dehydrate_selected do drugiego.)
  2. rozbrojony full=True na jednej z zasobu
  3. dodać partial_fields do zasobu Meta zasobu rozbrojony

`` `

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'