Django Tastypie throws "maximum recursion depth" when full = True by feedback.

I get the maximum recursion depth if you run the code below:

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 

If the set full = False in one of the ratios, this works. I understand why this happens, but I need both relationships to transfer data, not just "resource_uri". Is there a way for Tastypie to do this? I managed to solve the problem of creating a serialization method in my project model, but it is far from elegance. Thanks.

+6
source share
3 answers

You will have to override the full_dehydrate method with at least one resource to skip the dehydration-related resource that causes recursion.

Alternatively, you can define two types of resources that use the same model with full=True and the other with full=False .

+13
source

Thanks @astevanovic for pointing the right direction.

I found that overriding the dehydrate method to process only certain specified fields is a little less tedious than overriding the full_hydrate method to skip fields.

In an effort to reuse, I came up with the following code snippets. Hope this will be helpful for some:

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

When using the @sigmus example, resources will require 3 modifications:

  • both resources will use BeeModuleResource as their superclass (or add dehydrate_partial to one resource and dehydrate_selected to another.)
  • unset full=True for any of the resources
  • add partial_fields to Meta resource unset resource

`` ``

 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' 
+3
source

Dead simple solution: set use_in = 'list' kwarg to both relationship fields!

Docs: http://django-tastypie.readthedocs.org/en/latest/fields.html#use-in

+2
source

Source: https://habr.com/ru/post/920924/


All Articles