Django Restframework with nested json and custom key - python

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

Related

Nested JSON output for Primary and Foreign key Django Rest Framework

Iam using Django 3.2 with Restframework.
I have a Model as a for a Primary key named Tag and a Model connected as a foreign key named TagItem.
I tried to get a nested JSON output for it but its not working as expected.
Models.py
class Tags(models.Model):
tagId = models.CharField(primary_key=True, max_length=100,default=1)
section = models.CharField(max_length=100)
class TagItem(models.Model):
tagId= models.ForeignKey(Tags, on_delete=models.CASCADE,default=1)
select = models.BooleanField(default=False)
name = models.CharField(max_length=50)
views.py
def get_tag_details(request):
if request.method == 'GET':
tag = Tags.objects.all()
tagitem = TagItem.objects.all()
TagSerializeobj = TagsSerializer(tag,many=True)
TagItemSerializeobj = TagItemSerializer(tagitem, many=True)
result = {}
result['tag'] = TagSerializeobj.data
for item in TagItemSerializeobj:
if item.tagId == result['tag'].tagId:
result['tagItem'] = item
return Response(result)
Error: 'ListSerializer' object is not iterable
How to iterate of get items nested under related Tags only.
Required output
{
"id": 1,
"section": "crafts",
"items": [
{
"id": "10",
"select": false,
"name": "Wood"
},
{
"id": "11",
"select": false,
"name": "Clay"
}
]
},
{
"id": 2,
"section": "states",
"items": [
{
"id": "20",
"select": false,
"name": "Andhra Pradesh"
},
{
"id": "21",
"select": false,
"name": "Arunachal Pradesh"
},
]
}
You don't need to do all those processing in your views. ModelSerializer can do it for you, so you can just define a model serializer for TagItem that is nested on a model serializer for Tag so something like:
class TagItemModelSerializer(serializers.ModelSerializer):
class Meta:
model = TagItem
fields = '__all__'
class TagModelSerializer(serializers.ModelSerializer):
items = serializers.SerializerMethodField()
class Meta:
model = Tag
fields = '__all__'
def get_items(self, obj):
return TagItemModelSerializer(obj.tag_items.all(), many=True)
And in your views:
serializer = TagModelSerializer(Tags.objects.all(), many=True)
return Response(serializer.data)
I get unexpected keyword error when i tried to assign

how to access child entire record in parent model in django rest framework

I am new to Django rest framework.i am trying to get child model records to the parent model as a field so that all the RefreshmentImage models records are available in games_sports list.i have posted sample code.
model.py
class Refreshment(models.Model):
title = models.CharField(max_length=200, unique=True)
type = models.CharField(max_length=200)
charges = models.DecimalField(max_digits=12, decimal_places=2, help_text="Charges per hour")
class RefreshmentImage(models.Model):
refreshment = models.ForeignKey(Refreshment, on_delete=models.CASCADE)
image = models.FileField(upload_to="refreshment_image/", null=True, blank=True)
serializers.py
class EntertainmentSerializer(serializers.ModelSerializer):
class Meta:
model = Refreshment
fields = '__all__'
class RefreshmentImageSerializer(serializers.ModelSerializer):
refreshment = EntertainmentSerializer(read_only=True, many=True)
class Meta:
model = RefreshmentImage
fields = '__all__'
views.py
def all_games_sports(request):
entertainment = Refreshment.objects.all()
serialize = EntertainmentSerializer(instance=entertainment,many=True)
serial = RefreshmentImageSerializer(instance=entertainment,many=True)
main = {'status': True, 'code': CODE_SUCCESSFUL, 'msg': SUCCESS, 'games_sports': serialize.data,'image':serial.data}
return HttpResponse(json.dumps(main), content_type='application/json')
what i got is like:
games_sports": [
{
"id": 1,
"title": "yyy",
"type": 1,
"charges": "500.00",
},
{
"id": 2,
"title": "xxxxx",
"type": "something",
"charges": "501.00",
}
*******
],
"image": [
{
"id": 1,
"image": null,
"date_created": "2019-03-03T08:16:15.538024+05:30"
},
**********
]
i want it to be:
games_sports": [
{
"id": 1,
"title": "yyy",
"type": 1,
"charges": "500.00",
"image": [
{
"id": 1,
"image": image_path,
"date_created": "2019-03-03T08:16:15.538024+05:30"
},
}
***********
],
Try this snippet
#serializers.py
"""I've re-arranged the order of 'RefreshmentImageSerializer' serializer and 'EntertainmentSerializer' serializer"""
class RefreshmentImageSerializer(serializers.ModelSerializer):
class Meta:
model = RefreshmentImage
fields = '__all__'
class EntertainmentSerializer(serializers.ModelSerializer):
image = RefreshmentImageSerializer(many=True, source='refreshmentimage_set')
class Meta:
model = Refreshment
fields = '__all__'
# views.py
"""Added DRF stuffs such as 'api_view' and 'Response'"""
from rest_framework.decorators import api_view
#api_view()
def all_games_sports(request):
entertainment = Refreshment.objects.all()
serialize = EntertainmentSerializer(instance=entertainment, many=True)
main = {'status': True, 'code': "CODE_SUCCESSFUL", 'msg': "SUCCESS", 'games_sports': serialize.data}
return Response(main)
{
"status": true,
"code": "CODE_SUCCESSFUL",
"msg": "SUCCESS",
"games_sports": [
{
"id": 1,
"image": [
{
"id": 1,
"image": null,
"refreshment": 1
},
{
"id": 3,
"image": "refreshment_image/jpg-icon.png",
"refreshment": 1
}
],
"title": "t1",
"type": "tt1",
"charges": "123.00"
},
{
"id": 2,
"image": [
{
"id": 2,
"image": "refreshment_image/asd.jpg",
"refreshment": 2
}
],
"title": "t2",
"type": "tt2",
"charges": "321.00"
},
{
"id": 3,
"image": [
{
"id": 4,
"image": "refreshment_image/Screenshot_from_2018-10-26_16-32-41.png",
"refreshment": 3
},
{
"id": 5,
"image": "refreshment_image/twitter.png",
"refreshment": 3
}
],
"title": "t3",
"type": "tt3",
"charges": "754.00"
}
]
}
What I've done here?
re-arranged the order of serializer to avoid not defined errors
added a new field in EntertainmentSerializer class to show the images associated with the Refreshment object
In views.py I've added DRF pieces of stuff, which is more suitable
References
#api_view() decorator
DRF's Response() class
DRF Nested Serializers
The source keyword argument
Hope this helps!!

Customize the nested data in Serializer

I have a ModelSerializer:
class WorkOrderRetrieveSerializer(ModelSerializer):
workordercomments = WorkOrderCommentForWorkOrderSerializer(many=True, read_only=True)
class Meta:
model = WorkOrder
fields = "__all__"
The JSON data is bellow:
{
"id": 1,
"workordercomments": [
.....
{
"id": 21,
"content": "test files",
"files": "[71]",
"ctime": "2018-01-11T11:03:17.874268+08:00",
"uptime": "2018-01-11T11:03:17.874362+08:00",
"workorder": 1,
"comment_user": {
"id": 5,
"username": "test03",
"is_admin": true
}
}
],
"workorder_num": "WON15118747168252",
"name": "order01",
"content": "first conntetn",
"workordertype": "teck",
"workorder_status": "created",
"user_most_use_email": "lxas#128.com",
"server_id": null,
"public_ip": null,
"belong_area": null,
"files": null,
"ctime": "2017-11-28T21:11:56.826971+08:00",
"uptime": "2017-11-28T21:11:56.827064+08:00",
"to_group": 3,
"user": 2
}
The "files": "[71]", in my JSON is a string of a group contains file ids.
workordercomments is the related-name of the workorder.
I want in the JSON workordercomments shows the files like this:
{
"id": 21,
"content": "test files",
"files": "['/media/images/xxxxx.png']",
"ctime": "2018-01-11T11:03:17.874268+08:00",
"uptime": "2018-01-11T11:03:17.874362+08:00",
"workorder": 1,
"comment_user": {
"id": 5,
"username": "test03",
"is_admin": true
}
}
The "files" value I want to is the link rather than its id.
"files": "['/media/images/xxxxx.png']",
or
"files": ['/media/images/xxxxx.png'],
Is it possible to customize the format? should I come true what function in serializer ?
You need to implement a custom serialzier, as per the docs, and override the default values generated by ModelSerializer.
For example:
from rest_framework.fields import Field
class FileRelatedField(RelatedField):
def to_representation(self, instance):
return instance.file_path # or whereever that path comes from
class WorkOrderRetrieveSerializer(ModelSerializer):
class Meta:
model = WorkOrder
fields = '__all__'
files = FileRelatedField(
many=True,
source='file_set.all'
)
Depending on how your __str__ value is on your File model, you can maybe do this:
from rest_framework.serializers import StringRelatedField
class WorkOrderRetrieveSerializer(ModelSerializer):
class Meta:
model = WorkOrder
fields = '__all__'
files = StringRelatedField(
many=True,
source='file_set.all'
)

Django Rest Framework: Derived model serializer fields

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

Django rest framework, translate model with django-hvad

I have model Product:
class Product(TranslatableModel):
name = models.CharField(max_length=255, unique=True)
translations = TranslatedFields(
description=models.TextField(),
)
and in administration on product detail I have tabs with languages. For example tabs EN, CZ, each includes disctiption. So PUT request looks like:
{
'product': '1',
'id': 1,
'name': 'Name',
'translations': {
'cz': {'desctiption': 'Description cz'},
'en': {'desctiption': 'Description en'}
}
}
I founded in django-hvad TranslationsMixin that allows me to do that request.
in serializers I have:
class ProductTranslationSerializer(serializers.ModelSerializer):
class Meta:
exclude = ['description']
class ProductSerializer(TranslationsMixin, serializers.ModelSerializer):
class Meta:
model = Product
translations_serializer = ProductTranslationSerializer
fields = (
'name',
'description',
)
Question is how will looks ModelViewSet for this request? Can I choose language like 'language_code=en', filter that query and get something like:
[
{
id: 1
name: "name"
descritpion: "descritpion"
},
....
]
Thank you!
the most likely way i achieve is:
models.py
class Product(TranslatableModel):
category = models.ForeignKey('product.ProductCategory',
related_name='product_category',
null=True,
on_delete=models.SET_NULL,
verbose_name=u'category')
cover = models.ImageField(upload_to=product_cover,
null=True,
verbose_name=u'cover')
translations = TranslatedFields(
title=models.CharField(max_length=100,
null=True,
verbose_name=u'title'),
summary=models.TextField(null=True,
verbose_name=u'summary'),
model=models.CharField(max_length=255,
null=True,
blank=True,
verbose_name=u'model'),
price=models.DecimalField(default=0.00,
max_digits=10,
decimal_places=2,
blank=True,
validators=[MinValueValidator(0)],
verbose_name=u'price'),
content=models.TextField(verbose_name=u'content'),
publish_time=models.DateTimeField(default=timezone.now,
verbose_name=u'发布publish_time')
)
view_times = models.IntegerField(default=0,
verbose_name=u'view_times ')
views.py
class ProductViewSet(ModelViewSet):
serializer_class = ProductListSerializer
def get_queryset(self):
if 'language_code' in self.request.GET:
language_code = self.request.GET.get('language_code')
queryset = Product.objects.language(language_code).order_by('-id')
else:
queryset = Product.objects.language().order_by('-id')
return queryset
serializers.py
class ProductCategorySerializer(TranslatableModelSerializer):
class Meta:
model = ProductCategory
fields = '__all__'
class ProductListSerializer(TranslatableModelSerializer):
category = ProductCategorySerializer(read_only=True)
class Meta:
model = Product
exclude = ['is_abandon', 'content', ]
urls.py
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'product', ProductViewSet, base_name='api-product')
...
result:
http://192.168.1.108/api/product/?language_code=zh-hans you get:
{
"count": 1,
"page_num": 1,
"page_no": 1,
"next": "",
"previous": "",
"results": [
{
"id": 2,
"category": {
"id": 2,
"create_time": "2017-08-10 16:49:41",
"update_time": "2017-08-18 08:56:02",
"name": "测试",
"language_code": "zh-hans"
},
"create_time": "2017-08-18 08:53:46",
"update_time": "2017-08-18 08:56:28",
"cover": "http://192.168.1.108/media/product/20170818-085346-518_59.jpg",
"view_times": 0,
"title": "标题",
"summary": "简介",
"model": null,
"price": "90.00",
"publish_time": "2017-08-18 08:53:00",
"language_code": "zh-hans"
}
]
}
http://192.168.1.108/api/product/?language_code=en you get:
{
"count": 1,
"page_num": 1,
"page_no": 1,
"next": "",
"previous": "",
"results": [
{
"id": 2,
"category": {
"id": 2,
"create_time": "2017-08-10 16:49:41",
"update_time": "2017-08-18 08:56:02",
"name": "测试",
"language_code": "zh-hans"
},
"create_time": "2017-08-18 08:53:46",
"update_time": "2017-08-18 09:00:23",
"cover": "http://192.168.1.108/media/product/20170818-085346-518_59.jpg",
"view_times": 0,
"title": "title",
"summary": "summary",
"model": "model",
"price": "91.00",
"publish_time": "2017-08-18 08:56:04",
"language_code": "en"
}
]
}
this way fk will not change language,if you want fk change language too,use:
urls.py
urlpatterns += i18n_patterns(
'''
url(r'^api/', include(router.urls)),
prefix_default_language=False,
)
change language by
http://192.168.1.108/zh-hans/api/product/
to
http://192.168.1.108/en/api/product/
your will get:
{
"count": 1,
"page_num": 1,
"page_no": 1,
"next": "",
"previous": "",
"results": [
{
"id": 2,
"category": {
"id": 2,
"create_time": "2017-08-10 16:49:41",
"update_time": "2017-08-18 08:56:02",
"name": "test",
"language_code": "en"
},
"create_time": "2017-08-18 08:53:46",
"update_time": "2017-08-18 09:00:23",
"cover": "http://192.168.1.108/media/product/20170818-085346-518_59.jpg",
"view_times": 0,
"title": "title",
"summary": "summary",
"model": "model",
"price": "91.00",
"publish_time": "2017-08-18 08:56:04",
"language_code": "en"
}
]
}
Answer from owner of django-hvad github repo #spectras:
If I understand well, you want to use a different serialized form for the collection endpoint and the item endpoint. That is, GET-1, PUT, POST would use the translations mixin, while GET-many would not.
You need two have two different serializers for this:
The one you created
another one that would be, for instance,
class ProductItemSerializer(TranslatableModelSerializer): ...
another one that would be, for instance,
class ProductItemSerializer(TranslatableModelSerializer): ...
Given that, you can add to your ModelViewSet a method that dynamically chooses the serializer class depending on request type:
class SomeModelViewSet(ModelViewSet):
# serializer_class = not needed, we use the method instead
def get_serializer_class(self):
if self.action == 'list':
return ProductItemSerializer
return ProductSerializer
An alternate method, perhaps easier to use on the Javascript side, would be to make 2 viewsets for the Product model, a read-write one being translation aware (using ProductSerializer) and a read-only one not translation aware, using (ProductItemSerializer).
In the same vein, I usually only have translation-unaware serializers and add a /api/product/details/ endpoint that is translation-aware. This way, I only have to handle the complexity of a dict object when going into edit/detailed mode on the client side.

Categories

Resources