Serializing django-mptt trees in Tastypie

How to serialize django-mptt trees in Tastypie ?

I want to use django-mptt cache_tree_children() . I tried using Tastypie in different hooks, but that causes an error.

+4
source share
2 answers

Here's how I solved it:

 class MenuResource(ModelResource): parent = fields.ForeignKey('self', 'parent', null=True) class Meta: serializer = PrettyJSONSerializer() queryset = Menu.objects.all().select_related('parent') include_resource_uri = False fields = ['name'] def get_child_data(self, obj): data = { 'id': obj.id, 'name': obj.name, } if not obj.is_leaf_node(): data['children'] = [self.get_child_data(child) \ for child in obj.get_children()] return data def get_list(self, request, **kwargs): base_bundle = self.build_bundle(request=request) objects = self.obj_get_list(bundle=base_bundle, **self.remove_api_resource_names(kwargs)) sorted_objects = self.apply_sorting(objects, options=request.GET) paginator = self._meta.paginator_class( request.GET, sorted_objects, resource_uri=self.get_resource_uri(), limit=self._meta.limit, max_limit=self._meta.max_limit, collection_name=self._meta.collection_name ) to_be_serialized = paginator.page() from mptt.templatetags.mptt_tags import cache_tree_children objects = cache_tree_children(objects) bundles = [] for obj in objects: data = self.get_child_data(obj) bundle = self.build_bundle(data=data, obj=obj, request=request) bundles.append(self.full_dehydrate(bundle)) to_be_serialized[self._meta.collection_name] = bundles to_be_serialized = self.alter_list_data_to_serialize(request, to_be_serialized) return self.create_response(request, to_be_serialized) 

If you are not using pagination, you can simply accept this part. This is what I did.

+2
source

Without the cache_tree_children method cache_tree_children you could probably serialize your children by simply connecting ToManyField with full=True , specifying the children property:

 class MenuResource(ModelResource): children = fields.ToManyField('self', 'children', null=True, full=True) parent = fields.ToOneField('self', 'parent', null=True) class Meta: queryset = Menu.objects.all() 

To implement the cache_tree_children function, you can write your own subclass ToManyField , which overrides the standard dehydrate function. Please note that I tested this solution very superficially:

 def dehydrate(self, bundle): if not bundle.obj or not bundle.obj.pk: if not self.null: raise ApiFieldError("The model '%r' does not have a primary key and can not be used in a ToMany context." % bundle.obj) return [] the_m2ms = None previous_obj = bundle.obj attr = self.attribute if isinstance(self.attribute, basestring): attrs = self.attribute.split('__') the_m2ms = bundle.obj for attr in attrs: previous_obj = the_m2ms try: the_m2ms = getattr(the_m2ms, attr, None) except ObjectDoesNotExist: the_m2ms = None if not the_m2ms: break elif callable(self.attribute): the_m2ms = self.attribute(bundle) if not the_m2ms: if not self.null: raise ApiFieldError("The model '%r' has an empty attribute '%s' and doesn't allow a null value." % (previous_obj, attr)) return [] self.m2m_resources = [] m2m_dehydrated = [] # There goes your ``cache_tree_children`` for m2m in cache_tree_children(the_m2ms.all()): m2m_resource = self.get_related_resource(m2m) m2m_bundle = Bundle(obj=m2m, request=bundle.request) self.m2m_resources.append(m2m_resource) m2m_dehydrated.append(self.dehydrate_related(m2m_bundle, m2m_resource)) return m2m_dehydrated 

One of the main advantages of this method is that you no longer need to worry about the limitations / differences of the view / list. You can even parameterize this aspect of your resource until you get some default behavior that suits your needs. Based on the field, that is. I think this is cool.

+6
source

All Articles