Django Tastypie выдает «превышение максимальной глубины рекурсии», когда full=True в обратном отношении.

Я получаю превышение максимальной глубины рекурсии, если запускаю код ниже:

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

Если для одного из отношений установлено значение full=False, оно работает. Я понимаю, почему это происходит, но мне нужны оба отношения для передачи данных, а не только «resource_uri». Есть ли способ Tastypie сделать это? Мне удалось решить проблему, создав метод сериализации в моей модели проекта, но это далеко не элегантно. Спасибо.


person sigmus    schedule 19.07.2012    source источник


Ответы (3)


Вам придется переопределить метод full_dehydrate по крайней мере для одного ресурса, чтобы пропустить обезвоживание связанного ресурса, который вызывает рекурсию.

В качестве альтернативы вы можете определить два типа ресурсов, использующих одну и ту же модель: один с full=True, а другой с full=False.

person astevanovic    schedule 19.07.2012
comment
Спасибо за совет с двумя ресурсами, это мне очень помогло... :) - person Mark Shust at M.academy; 14.09.2012
comment
@MarkShust: я столкнулся с похожей проблемой, не могли бы вы подробнее рассказать о каком-либо из методов, которые сработали для вас? - person Nikhil Agrawal; 16.07.2015
comment
@NikhilAgrawal, если я правильно помню, я только что сделал два ресурса, один с полным = True, один с полным = False, с суффиксом full = False с Simple, чтобы я знал, какой ресурс есть что. затем сослаться на соответствующий ресурс. - person Mark Shust at M.academy; 16.07.2015

Спасибо @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'
person Thomas - BeeDesk    schedule 18.07.2013

Очень простое решение: установите use_in = 'list' kwarg в обоих полях отношений!

Документы: http://django-tastypie.readthedocs.org/en/latest/fields.html#use-in

person Adam    schedule 05.06.2014