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'
)
Related
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']
I'm learning how to use Django Rest Framework and I came out with a question that I can't get to answer.
I understood that the concept of nested serializer but I think there should be a way to get a specific field in an "upper level" instead of "digging" trough levels. Let me better explain what I mean.
I have Users which then are classified into 2 categories Customers (which are the default User category) and Drivers. All the Users have a Profile with their picture so I would like to obtain the name and the image in an API. The only solution I've been managing to find is the following
class OrderProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ("id", "image")
class OrderUserSerializer(serializers.ModelSerializer):
profile = OrderProfileSerializer()
class Meta:
model = User
fields = ("id", "profile")
class OrderCustomerSerializer(serializers.ModelSerializer):
name = serializers.ReadOnlyField(source="get_full_name")
profile = OrderProfileSerializer()
class Meta:
model = User
fields = ("id", "name", "profile")
class OrderDriverSerializer(serializers.ModelSerializer):
name = serializers.ReadOnlyField(source="user.get_full_name")
user = OrderUserSerializer()
class Meta:
model = Driver
fields = ("id", "name", "user")
This is the JSON response
{
"shipping": {
"id": 25,
"customer": {
"id": 14,
"name": "Test Customer",
"profile": {
"id": 10,
"image": "/media/profile_pics/default.jpg"
}
},
"driver": {
"id": 1,
"name": "Test Driver",
"user": {
"id": 28,
"profile": {
"id": 11,
"image": "/media/profile_pics/default.jpg"
}
}
}
}
}
I can make it to work anyway, but I believe that to learn I should always push for a more "elegant" solution rather than "keep the easiest". So I would like to achieve the following response:
{
"shipping": {
"id": 25,
"customer": {
"id": 14,
"name": "Test Customer",
"image": "/media/profile_pics/default.jpg"
},
"driver": {
"id": 1,
"name": "Test Driver",
"image": "/media/profile_pics/default.jpg"
}
}
}
Thank you for any tips/help/suggest that you can provide
Instead of using:
profile = OrderProfileSerializer()
re-write to:
image = serializers.SerializerMethodField()
def get_image(self, obj):
# obj is the customer object
# return what u need (like this example) or write a model inner method
return obj.profile.image
This would align nicely to the json you want
Do the same thing for Driver and User
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!!
I need to add additional fields that are not part of my model to the json response. Is this possible? The models are connected using multiple tables, but do not have a direct connection.
models.py
class Message(models.Model):
body = models.TextField(max_length=500, blank=False)
sender = models.ForeignKey(User)
timestamp = models.DateTimeField(auto_now_add=True)
conversation = models.ForeignKey(Conversation)
serializers.py
class MessageSerializer(serializers.ModelSerializer):
conversation = serializers.ReadOnlyField(source='conversation.id')
sender = serializers.ReadOnlyField(source='sender.id')
# task = TaskSerializer(source='conversation__task_worker__task.id')
# This commented code above is not working, but this is what I need.
class Meta:
model = Message
fields = '__all__'
So what I need, is to pass the value of task, which can be found by going back a few tables. E.g. if the current model is Message, I need to:
Message -> Conversation -> Task_worker -> Task (Get this)
My current json response is:
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"conversation": 1,
"sender": 2,
"body": "Hello There",
"timestamp": "2018-01-31T17:48:19.680113Z"
},
{
"id": 3,
"conversation": 1,
"sender": 2,
"body": "Can you do the task",
"timestamp": "2018-02-01T09:39:24.797218Z"
}
]
}
Ideally, I would like the following response:
{
"count": 2,
"next": null,
"previous": null,
"conversation": 1,
"task": 5,
"any_extra_fields_key": 'value',
"results": [
{
"id": 1,
"sender": 2,
"body": "Hello There",
"timestamp": "2018-01-31T17:48:19.680113Z"
},
]
}
The response is after (list view): GET /api/conversations/<conversation-id>/messages/
EDIT: added views.py
class MessageList(generics.ListCreateAPIView):
serializer_class = MessageSerializer
def get_queryset(self):
conversation = Conversation.objects.get(pk=self.kwargs['conv_id'])
queryset = Message.objects.filter(conversation=conversation)
return queryset
def perform_create(self, serializer):
conversation = Conversation.objects.get(pk=self.kwargs['conv_id'])
serializer.save(sender=self.request.user, conversation=conversation)
EDIT 2:
class MessageSerializer(serializers.ModelSerializer):
sender = serializers.ReadOnlyField(source='sender.id')
children_list = serializers.SerializerMethodField('get_children')
def get_children(self, obj):
serializer = TaskSerializer(source='conversation__task_worker__task', many=True)
return serializer.data
class Meta:
model = Message
fields = ['children_list','sender','conversation','timestamp']
outputs:
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"children_list": [],
"sender": 2,
"conversation": 1,
"timestamp": "2018-01-31T17:48:19.680113Z"
},
{
"children_list": [],
"sender": 2,
"conversation": 1,
"timestamp": "2018-02-01T09:39:24.797218Z"
}
]
}
First of all, children list is empty, but it should be the task, and also it's still inside the 'results' key, creating a lot of duplication, since the children_list is the same task.
SerializerMethodField is what I needed to get me the data that I want. However, the structure is still a problem
class MessageSerializer(serializers.ModelSerializer):
sender = serializers.ReadOnlyField(source='sender.id')
task_id = serializers.SerializerMethodField()
def get_task_id(self, obj):
task = Task.objects.filter(task_worker__conversation__message=obj)[0]
return task.id
class Meta:
model = Message
fields = ['task_id','sender','conversation','timestamp']
I'm looking for suggestions on how to sort/group serialized fields by value.
Here's a code example explaining what I want to achieve.
Models
class Folder(models.Model):
name = models.CharField()
class File(models.Model):
filetype = models.CharField()
name = models.CharField()
folder = models.ForeignKey(Folder)
Serializers
class FileSerializer(serializers.ModelSerializer):
class Meta:
model = File
fields = ('id', 'filetype', 'name')
class FolderSerializer(serializers.ModelSerializer):
files = FileSerializer(read_only=True)
class Meta:
model = Folder
fields = ('name', 'files')
This serializes to:
{
"name": "Test Folder",
"files": [
{"id": 1, "filetype": "pdf", "name": "some pdf file"}.
{"id": 2, "filetype": "pdf", "name": "some other pdf file"},
{"id": 3, "filetype": "txt", "name": "some text file"}
]
}
I'm looking for a way to serialize to this:
{
"name": "Test Folder",
"files": [
"pdf": [
{"id": 1, "name": "some pdf file"},
{"id": 2, "name": "some other pdf file"}
],
"txt": [
{"id": 3, "name": "some text file"}
]
]
}
Try to use SerializerMethodField for this. You need to implement something like this:
class FolderSerializer(serializers.ModelSerializer):
files = serializers.SerializerMethodField()
class Meta:
model = Folder
fields = ('name', 'files')
def get_files(self, obj):
result = {'pdf': [], 'txt':[]}
for file in obj.file_set.all():
serializer = FileSerializer(file)
if file.name.endswith('pdf'):
result['pdf'].append(serializer.data)
if file.name.endswith('txt'):
result['txt'].append(serializer.data)
return result