2012-07-19 10 views
6

me siento un nivel de recursividad superado si una carrera por debajo del código:Django Tastypie arroja una "profundidad máxima de recursión excedida" cuando está llena = Verdadero en relación inversa.

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 

Si un conjunto completo = False en una de las relaciones que trabaja. Entiendo por qué sucede esto, pero necesito ambas relaciones para traer datos, no solo el "resource_uri". ¿Hay una forma de hacerlo de Tastypie? Logré resolver el problema creando un método de serialización en mi Modelo de Proyecto, pero está lejos de ser elegante. Gracias.

Respuesta

13

Debería sobrescribir el método full_dehydrate en al menos un recurso para omitir el recurso relacionado con la deshidratación que está causando la recursión.

Alternativamente, puede definir dos tipos de recursos que utilizan el mismo modelo uno con full=True y otro con full=False.

+3

Gracias por la punta de dos recursos, esto me ha ayudado mucho ... :) –

+0

@MarkShust: Estoy frente a el mismo tipo de problema, puede por favor elaborar más en cualquiera de el método que funcionó para ti? –

+0

@NikhilAgrawal si mal no recuerdo, acabo de hacer dos recursos, uno con full = True, uno con full = False, con el sufijo full = False con Simple, así que sé qué recurso es qué. luego haz referencia al recurso apropiado. –

3

Gracias @astevanovic apuntando en la dirección correcta.

Encontré que reemplazar el método dehydrate para procesar solo algunos campos especificados es un poco menos tedioso que anular el método full_hydrate para omitir campos.

En la búsqueda de la reutilización, se me ocurrieron los siguientes fragmentos de código. Espero que sería útil para algunos:

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

Con @sigmus ejemplo, los recursos se necesitan 3 modificaciones:

  1. tanto de recursos utilizará BeeModuleResource como su superclase (o, añadir dehydrate_partial a uno recursos y dehydrate_selected a la otra.)
  2. desarmar full=True en cualquiera de los recursos
  3. partial_fields añadir en el recurso Meta el recurso unset

`` `

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' 
Cuestiones relacionadas