Reputation: 11
I searched for a similar question without success.. So, i am working on a website in which i am using django-mptt to organize categories. My Model looks like:
class Category(MPTTModel):
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, related_name='children')
name = models.CharField(max_length=90)
slug = models.SlugField(unique=True)
_full_slug_separator = '/'
@property
def url(self):
names = [category.name for category in self.get_ancestors(include_self=True)]
return self._full_slug_separator.join(names)
I defined the CategorySerializer as bellow:
class CategorySerializer(serializers.ModelSerializer):
children = serializers.SerializerMethodField()
class Meta:
model = Category
fields = ('name', 'url', 'children')
def get_children(self, obj):
return CategorySerializer(obj.get_children(), many=True).data
# views.py
class CategoryList(generics.ListAPIView):
queryset = Category.objects.root_nodes()
serializer_class = CategorySerializer
The question is how can i:
1. have the 'url' data included in leaf nodes only.
2. have the 'children' data included in non leaf nodes only.
Here is an example of the output I am looking for
[
{
"title":"root node",
"children":[
{
"title":"leaf node",
"url":"link"
},
{
"title":"non leaf node",
"children":[
{
"title":"leaf node",
"url":"link"
}
]
},
{
"title":"non leaf node",
"children":[
{
"title":"non leaf node",
"children":[
{
"title":"leaf node",
"url":"link"
}
]
}
}
]
},
{
"title":"root node",
"url":"link"
}
]
Also i want to know if there is a good way for generating the 'url' to reduce queries
And thanks for any help in advance.
Upvotes: 1
Views: 1023
Reputation: 72
I ran into the same problem and found this question. But there were no answers. So, I am posting how I managed to do it for anyone who may need it in the future. This is most likely not the best solution but it works.
My models.py
:
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _
from mptt.models import MPTTModel, TreeForeignKey
class Category(MPTTModel):
name = models.CharField(max_length=100, null=False, blank=False, verbose_name=_("category name"), help_text=_("format: required, max_length=100"))
slug = models.SlugField(max_length=150, null=False, blank=False, editable=False, verbose_name=_("category url"), help_text=_("format: required, letters, numbers, underscore or hyphen"))
parent = TreeForeignKey("self", on_delete=models.SET_NULL, related_name="children", null=True, blank=True, verbose_name=_("parent category"), help_text=_("format: not required"))
class MPTTMeta:
order_insertion_by = ['name']
class Meta:
verbose_name = _('product category')
verbose_name_plural = _('product categories')
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Category, self).save(*args, **kwargs)
def __str__(self):
return self.name
My serializers.py
:
from rest_framework import serializers
class SubCategorySerializer(serializers.ModelSerializer):
"""Serializer for lowest level category that has no children."""
parent = serializers.SerializerMethodField(source='get_parent')
class Meta:
model = Category
fields = ['id', 'name', 'slug', 'parent', ]
def get_parent(self, obj):
if obj.parent:
return obj.parent.name
class CategorySerializer(serializers.ModelSerializer):
"""Serializer for category."""
parent = serializers.SerializerMethodField(source='get_parent')
children = serializers.SerializerMethodField(source='get_children')
class Meta:
model = Category
fields = ['id', 'name', 'slug', 'parent', 'children', ]
def get_parent(self, obj):
if obj.parent:
return obj.parent.name
def get_children(self, obj):
if obj.children.exists():
children = [child for child in obj.children.all()]
children_with_children = [child for child in children if child.children.exists()]
children_without_children = [child for child in children if not child.children.exists()]
if children_with_children:
return CategorySerializer(children_with_children, many=True).data
if children_without_children:
return SubCategorySerializer(children_without_children, many=True).data
This way children
is included in non-leaf nodes only.
Here is an example of how the data looks:
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 4,
"name": "Clothes",
"slug": "clothes",
"parent": null,
"children": [
{
"id": 5,
"name": "Men's Clothes",
"slug": "mens-clothes",
"parent": "Clothes",
"children": [
{
"id": 7,
"name": "Men's Jeans",
"slug": "mens-jeans",
"parent": "Men's Clothes"
}
]
},
{
"id": 6,
"name": "Women's Clothes",
"slug": "womens-clothes",
"parent": "Clothes",
"children": [
{
"id": 8,
"name": "Women's Jeans",
"slug": "womens-jeans",
"parent": "Women's Clothes"
}
]
}
]
},
{
"id": 1,
"name": "Technology",
"slug": "technology",
"parent": null,
"children": [
{
"id": 3,
"name": "Laptop",
"slug": "laptop",
"parent": "Technology"
},
{
"id": 2,
"name": "Mobile Phone",
"slug": "mobile-phone",
"parent": "Technology"
}
]
}
]
}
Upvotes: 0