Django Rest Framework: Derived model serializer fields - python

I'm working on building a tree-like hierarchical database system using Django Rest Framework and django-polymorphic-tree. I have two models- BaseTreeNode and DescriptionNode (the later one is derived from BaseTreeNode). Specifically, here's my models.py:
class BaseTreeNode(PolymorphicMPTTModel):
parent = PolymorphicTreeForeignKey('self', blank=True, null=True, related_name='children', verbose_name=_('parent'))
title = models.CharField(_("Title"), max_length=200)
def __str__(self):
return "{}>{}".format(self.parent, self.title)
class Meta(PolymorphicMPTTModel.Meta):
verbose_name = _("Tree node")
verbose_name_plural = _("Tree nodes")
# Derived model for the tree node:
class DescriptionNode(BaseTreeNode):
description = models.CharField(_("Description"), max_length=200)
class Meta:
verbose_name = _("Description node")
verbose_name_plural = _("Description nodes")
So, each title field (belonging to BaseTreeNode) has an associated description field (belonging to DescriptionNode) with it.
Now, all I want to have is a JSON that returns a nested representation of my entire tree.
For now, I have only defined a simple serializer with a recursive field.
My serializers.py
from rest_framework_recursive.fields import RecursiveField
class DescriptionNodeSerializer(serializers.ModelSerializer):
class Meta:
model = DescriptionNode
fields = ('description',)
class BaseTreeNodeSerializer(serializers.ModelSerializer):
subcategories = serializers.ListSerializer(source="children",child=RecursiveField())
class Meta:
model = BaseTreeNode
fields = ('id', 'title', 'subcategories')
Which gives me (for BaseTreeNodeSerializer only):
[
{
"id": 1,
"title": "Apple",
"subcategories": [
{
"id": 2,
"title": "Contact Person",
"subcategories": []
},
{
"id": 3,
"title": "Sales Stage",
"subcategories": [
{
"id": 4,
"title": "Suspecting",
"subcategories": [
{
"id": 5,
"title": "Contact verification",
"subcategories": []
}
]
},
{
"id": 6,
"title": "Prospecting",
"subcategories": [
{
"id": 7,
"title": "Client Detail",
"subcategories": []
}
]
}
]
},
{
"id": 9,
"title": "Medium",
"subcategories": [
{
"id": 10,
"title": "Status",
"subcategories": []
}
]
},
{
"id": 13,
"title": "Remainder",
"subcategories": []
}
]
}
]
My question is, how can I include the description field (from the derived model) which is associated with every single title field (from the BaseTreeNode model) in the hierarchy?
Something like:
... {
"id": 5,
"title": "Contact verification",
"description": "Verified"
"subcategories": []
} ...

The Corresponding Model would be as follows:
class DescriptionNode(BaseTreeNode):
basetreenode = models.OneToOneField(BaseTreeNode, related_name="base_tree")
description = models.CharField(_("Description"), max_length=200)
class Meta:
verbose_name = _("Description node")
verbose_name_plural = _("Description nodes")
Serializer would be as follows:
from rest_framework_recursive.fields import RecursiveField
class BaseTreeNodeSerializer(serializers.ModelSerializer):
description = serializers.SerializerMethodField()
subcategories = serializers.ListSerializer(source="children",child=RecursiveField())
class Meta:
model = BaseTreeNode
fields = ('id', 'title', 'description', 'subcategories')
def get_description(self, obj):
return obj.base_tree.description #base_tree is related name of basetreenode field

Related

Django Restframework with nested json and custom key

Iam using Django with Restframework.
Iam trying to get a json output from serializers in a nested manner with key as one of the fields from model. I acheived the nested JSON but the json key for them is troubling me.
Here is my code and expected results out of it:
Models.py
class Tags(models.Model):
tagId = models.CharField(primary_key=True, max_length=100, default=1)
section = models.CharField(max_length=100,default=1)
def __str__(self):
return self.section
class TagItem(models.Model):
section= models.ForeignKey(Tags, on_delete=models.CASCADE,default=1,related_name="items")
select = models.BooleanField(default=False)
name = models.CharField(max_length=50)
def __str__(self):
return self.name
serializers.py
class TagItemModelSerializer(serializers.ModelSerializer):
class Meta:
model = TagItem
fields = '__all__'
class TagModelSerializer(serializers.ModelSerializer):
items = TagItemModelSerializer(many=True)
class Meta:
model = Tags
fields = ['pk','section', 'items']
Expected output:
{
"crafts" : { //craft is comming from Tag model's "section"
"id": 1,
"section": "crafts",
"items": [
{
"id": "10",
"select": false,
"name": "Wood",
"category": "crafts"
},
]
},
"states" : {
"id": 2,
"section": "states",
"items": [
{
"id": "20",
"select": false,
"name": "Andhra Pradesh",
"category": "states"
}
]
},
"others" : {
"id": 3,
"section": "others",
"items": [
{
"id": "30",
"select": false,
"name": "Volunteer",
"category": "others"
}
]
}
}
Current output:
[ //cant get the key of Tag model's "section"
{
"pk": "1",
"section": "states",
"items": [
{
"id": 6,
"select": false,
"name": "Assam",
"section": "1"
}
]
},
{
"pk": "2",
"section": "others",
"items": [
{
"id": 12,
"select": false,
"name": "Artisan",
"section": "2"
}
]
},
{
"pk": "3",
"section": "crafts",
"items": [
{
"id": 9,
"select": false,
"name": "Metal",
"section": "3"
}
]
}
]
At your case, you would like to have a custom representation I think?
You can adjust it with overriding to_representation()
I provide two approaches:
serializer -> the better way but not quite the output you prefer
view
class TagModelView(views.APIView):
def get(self, request):
qs = Tags.objects.all()
serializer = TagModelSerializer(qs, many=True)
return Response(data=serializer.data)
serializer
class TagItemModelSerializer(serializers.ModelSerializer):
category = serializers.SerializerMethodField()
class Meta:
model = TagItem
fields = ['id', 'select', 'name', 'category']
def get_category(self, instance):
return instance.section.section
class TagModelSerializer(serializers.ModelSerializer):
items = TagItemModelSerializer(many=True)
class Meta:
model = Tags
fields = ['pk', 'section', 'items']
def to_representation(self, instance):
container = super().to_representation(instance)
return {container.get('section'): container}
Making representation in your view -> output you would like to get
view
def get(self, request):
qs = Tags.objects.all()
serializer = TagModelSerializer(qs, many=True)
container = dict()
for element in serializer.data:
container.update(**element)
return Response(data=container)
serializer
No change to my proposal obove.
update
I saw that your primary key of Tags is tagId and you are using fields = ['pk', 'section', 'items'] but you would like to have id as a key-name.
Relabeling can help you.
class TagModelSerializer(serializers.ModelSerializer):
items = TagItemModelSerializer(many=True)
id = serializers.CharField(source='tagId')
class Meta:
model = Tags
fields = ['id', 'section', 'items']

How to make the category itself in the Parent Category and not just the id

I'm using Django Rest Framework for API and I wrote the model Category in which I have parent and I'm using it like this: parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
In API it looks like this:
{
"id": 7,
"name": "softwares",
"img": "",
"parent": 1
},
{
"id": 8,
"name": "databases",
"img": "",
"parent": 1
},
{
"id": 9,
"name": "appearance",
"img": "",
"parent": 2
},
{
"id": 10,
"name": "media",
"img": "",
"parent": 2
},
{
"id": 11,
"name": "system",
"img": "",
"parent": 2
},
Here is Category Serializer:
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__all__'
And Category View Set:
class CategoryViewSet(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
action_to_serializer = {
"retrieve": CategoryDetailSerializer,
}
def get_serializer_class(self):
return self.action_to_serializer.get(
self.action,
self.serializer_class
)
How can I make the category itself and not just the id?
Assuming you won't be handling with an infinite sequence of relations and are handling this with just one level of parenting, you can achieve that by using nested serializers, an example on your scenario would be something like this:
class NestedCategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('id', 'name', 'img')
class CategorySerializer(serializers.ModelSerializer):
parent = NestedCategorySerializer()
class Meta:
model = Category
fields = '__all__'
If your relationship needs more depth levels, you can improve this example to include always the serialized parent - be careful with cycles.

Django REST API populate only selected fields from Many to many field

I have a working API in my project that look something like this :
{
"id": 1,
"project_name": "Project A",
"user": [
{
"id": 3,
"employee_id": "001",
"official_name": "RAY PAlMER",
"nick_name": "RAY",
"date_of_birth": "1965-08-25",
"gender": "1",
"race": "CAUCASIAN",
"food_pref": "Vegetarian",
},
{
"id": 3,
"employee_id": "002",
"official_name": "LAIRA OMOTO",
"nick_name": "LAIRA",
"date_of_birth": "1990-01-15",
"gender": "2",
"race": "WHITE",
"food_pref": "Any",
}
]
}
The user field is a foreign key field and its populating everything from its source model called Employee. My question is how can I populate the user many to many field with only several field that I wanted to get from the Employee Model. For example I just want to populate only a user id, official name, and gender, therefore the output should be something like this
{
"id": 1,
"project_name": "Project A",
"user": [
{
"id": 3,
"official_name": "RAY PAlMER",
"gender": "1",
},
{
"id": 4,
"official_name": "LAIRA",
"gender": "2",
}
]
}
Below is my source code :
Project Model :
class Project(models.Model):
user = models.ManyToManyField(Employee, blank=True)
Employee Model :
class Employee(models.Model):
official_name = models.CharField(max_length=200, null=True, blank=True)
gender_choice = (
('male', 'Male'),
('female', 'Female')
)
gender = models.CharField(max_length=10, choices=gender_choice, null=True,
blank=True)
Serializer :
class MarketingReportSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ('id', 'project_name','user')
depth = 1
Any help is much appreciated thanks!
API :
class MarketingReportAPI(APIView):
def get(self, request):
all_projects = Project.objects.all()
project_serializer = MarketingReportSerializer(all_projects, many=True)
return Response(project_serializer.data)
Create EmployeeSerializer serializer.
class EmployeeSerializer(serializers.ModelSerializer):
class Meta:
model = Employee
fields = ('id', 'official_name', 'gender')
Now use it in your MarketingReportSerializer
class MarketingReportSerializer(serializers.ModelSerializer):
user = EmployeeSerializer(many=True)
class Meta:
model = Project
fields = ('id', 'project_name', 'user')
And then you can use MarketingReportSerializer in your view.

customize Django Rest Framework serializers output?

I have a Django model like this:
class Sections(models.Model):
section_id = models.CharField(max_length=127, null=True, blank=True)
title = models.CharField(max_length=255)
description = models.TextField(null=True, blank=True)
class Risk(models.Model):
title = models.CharField(max_length=256, null=False, blank=False)
section = models.ForeignKey(Sections, related_name='risks')
class Actions(models.Model):
title = models.CharField(max_length=256, null=False, blank=False)
section = models.ForeignKey(Sections, related_name='actions')
And serializers like that :
class RiskSerializer(serializers.ModelSerializer):
class Meta:
model = Risk
fields = ('id', 'title',)
class ActionsSerializer(serializers.ModelSerializer):
class Meta:
model = Actions
fields = ('id', 'title',)
class RiskActionPerSectionsSerializer(serializers.ModelSerializer):
risks = RiskSerializer(many=True, read_only=True)
actions = ActionsSerializer(many=True, read_only=True)
class Meta:
model = Sections
fields = ('section_id', 'risks', 'actions')
depth = 1
When accessing the RiskActionPerSectionSerializer over a view, I get the following output:
[
{
"actions": [
{
"id": 2,
"title": "Actions 2"
},
{
"id": 1,
"title": "Actions 1"
}
],
"risks": [
{
"id": 2,
"title": "Risk 2"
},
{
"id": 1,
"title": "Risk 1"
}
],
"section_id": "section 1"
}
.....
]
It s fine but I would prefer to have that :
{
"section 1": {
"risks": [
{
"id": 2,
"title": "Risk 2"
},
{
"id": 1,
"title": "Risk 1"
}
],
"actions": [
{
"id": 2,
"title": "Actions 2"
},
{
"id": 1,
"title": "Actions 1"
}
]
}
}
How can I do that with Django Rest Framework ?
You could override to_representation() method of your serializer:
class RiskActionPerSectionsSerializer(serializers.ModelSerializer):
class Meta:
model = Sections
fields = ('section_id', 'risks', 'actions')
depth = 1
def to_representation(self, instance):
response_dict = dict()
response_dict[instance.section_id] = {
'actions': ActionsSerializer(instance.actions.all(), many=True).data,
'risks': RiskSerializer(instance.risks.all(), many=True).data
}
return response_dict

Django REST - How can I get a JSON with two models?

I've two models (Map and Place) and I want to create a JSON with both of these (a map contains several places).
models.py
class Map(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(max_length=200)
class Place(models.Model):
map = models.ForeignKey('main.Map')
name = models.CharField(max_length=100)
slug = models.SlugField(max_length=200)
I use this to serialize these models individually :
serializers.py
class MapSerializer(serializers.ModelSerializer):
class Meta:
model = Map
fields = ('id', 'name')
class PlaceSerializer(serializers.ModelSerializer):
class Meta:
model = Place
fields = ('id', 'name', 'map')
I want a JSON like this, but I've no idea how to serialize this correctly...
{
"maplist": {
"maps": [
{
"id": "1",
"name": "dust2",
"places": [
{
"id": "1",
"name": "Long"
},
{
"id": "2",
"name": "Middle"
}
]
},
{
"id": "2",
"name": "inferno",
"places": [
{
"id": "1",
"name": "Middle"
},
{
"id": "2",
"name": "ASite"
}
]
}
]
}
}
Thanks in advance for your help.
Try with nested serializers:
class PlaceSerializer(serializers.ModelSerializer):
class Meta:
model = Place
fields = ('id', 'name')
class MapSerializer(serializers.ModelSerializer):
places = PlaceSerializer(many=True)
class Meta:
model = Map
fields = ('id', 'name')
To make this work, you need to change your model to include a related name to your foreign key:
class Place(models.Model):
map = models.ForeignKey('main.Map', related_name="places")
name = models.CharField(max_length=100)
slug = models.SlugField(max_length=200)

Categories

Resources