Group by in serializer in Django Rest Framework - django

In Django Rest Framework, I want to group my serializer data by date, How can I do that.
I have to group it by date on the basis of timestamp date.
my model: mongo model with some more fields also but only uses these two fields
class MyModel(Document):
#mongo model
candidate_id = StringField(required=True, db_field='c')
timestamp = DateTimeField(required=True, db_field='d')
...
... more fields
My Views: my views file
class MyView(generics.ListAPIView):
serializer_class = MySerializer
def get_queryset(self):
recruiter_id = self.request.GET.get("recruiter_id")
activity_list = MyModel.objects.filter(recruiter_id=str(recruiter_id)).order_by('- timestamp')
return activity_list
my serializer: using serializermethod field to get data from other DB by using candidate id from activity model
class MySerializer(serializers.Serializer):
candidate_id = serializers.CharField()
name = serializers.SerializerMethodField()
designation = serializers.SerializerMethodField()
age = serializers.SerializerMethodField()
timestamp = serializers.DateTimeField(format="%H:%M")
def get_name(self, obj):
return something
def get_designation(self, obj):
return something
def get_age(self, obj):
return something
I want to group my response by Date like :
[
{
"date": "2022-12-05",
"candidates":[
{
"candidate_id": 1,
"name": "One",
"designation": "Lead",
"age": 30,
"timestamp": "14:30"
}
]
},
{
"date": "2022-12-04",
"candidates":[
{
"candidate_id": 2,
"name": "Two",
"designation": "Lead",
"age": 30,
"timestamp": "14:30",
}
]
}
]
my actual response is like:
[
{
"candidate_id": 1,
"name": "One",
"designation": "Lead",
"age": 30,
"activity_timestamp": "13:12"
},
{
"candidate_id": 2,
"name": "Two",
"designation": "Lead",
"age": 30,
"activity_timestamp": "13:12"
}
]

Related

Django Rest Framework: Modify Serializer to return dictionary using a field as the key instead of an array of objects

Currently I have an API View that returns a dictionary that looks like this:
[
{
"id": 1,
"name": "Fly in the sky",
"thumbnail": "games/fly_in_the_sky",
"maps": [
{
"id": 1,
"name": "Blue Sea",
"thumbnail": "maps/blue_sea.jpg"
},
{
"id": 2,
"name": "Red Mountain",
"thumbnail": "maps/red_mountain.jpg"
}
],
"characters": [
{
"id": 1,
"name": "Steve",
"thumbnail": "characters/steve.jpg"
},
{
"id": 2,
"name": "Peter",
"thumbnail": "characters/peter.jpg"
}
]
}
]
I would like it to look like this instead, moving the names of all items as keys. (I would make sure ahead of time that all the names are unique so there is no key conflict)
{
"Fly in the sky": {
"id": 1,
"thumbnail": "games/fly_in_the_sky",
"maps": {
"Blue Sea": {
"id": 1,
"thumbnail": "maps/blue_sea.jpg"
},
"Red Mountain": {
"id": 2,
"thumbnail": "maps/red_mountain.jpg"
}
},
"characters": {
"Steve": {
"id": 1,
"thumbnail": "characters/steve.jpg"
},
"Peter",{
"id": 2,
"thumbnail": "characters/peter.jpg"
}
}
}
}
Is there any way of telling Django to return elements as a dictionary using a field as keys instead of an array?
My views and serializer currently look like this:
class GamesView(APIView):
def get(self, request):
game_objects = Game.objects.all()
game_serializer = GameListSerializer(game_objects, many=True)
return Response(game_serializer.data)
class MapListSerializer(ModelSerializer):
class Meta:
model = Map
fields = ('id', 'name', 'thumbnail')
class CharacterListSerializer(ModelSerializer):
class Meta:
model = Character
fields = ('id', 'name', 'thumbnail')
class GameListSerializer(ModelSerializer):
maps = MapListSerializer(many=True)
characters = CharacterListSerializer(many=True)
class Meta:
model = Game
fields = ('id', 'name', 'thumbnail', 'maps', 'characters')
When you give the parameter many=True to serializer, it returns a list and you cannot change it. This is better because more than one object can return. I wouldn't suggest it, but if you want to return the dictionary when there is only one element, you can write a condition like below. The reason I don't recommend it that it can be difficult to parse JSON for the client.
class GamesView(APIView):
def get(self, request):
game_objects = Game.objects.all()
game_serializer = GameListSerializer(game_objects, many=True)
serializer_data = game_serializer.data
if len(serializer_data) == 1:
serializer_data = serializer_data[0]
return Response(serializer_data)
Maybe this should help.
But i'm not sure why you would do that. xD
class GamesView(APIView):
def get(self, request):
game_objects = Game.objects.all()
game_serializer = GameListSerializer(game_objects, many=True)
modified_response = {}
if game_serializer.data:
for game in game_serializer:
name = game.pop("name")
modified_response[name] = game
return Response(modified_response)

Reformat Django REST Framework Serializer to get Output

I am trying to get data in a particular format but i'm not able to get the desired output.
My Model:
class Category(models.Model):
name = models.CharField(max_length=40)
class Expense(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="category_name")
description = models.CharField(max_length=200)
total_amount = models.IntegerField()
class Expense_Details(models.Model):
expense = models.ForeignKey(Expense, on_delete=models.CASCADE, related_name="payment")
user = models.IntegerField()
amount = models.FloatField()
type = models.CharField(max_length=100) ---->type is owe or lend
When I request /api/expenses/:
Expected Output
{
“total_expenses”: 10,
“Expenses”:
[{
“id”: 1,
“category”: 1,
“created_by”: 1, ------> user logged id
“description”: “lunch”,
“total_amount”: “105”,
“owe”: [{
“user_id”: 1,
“amount”: 10
},
{
“user_id”: 2,
“amount”: 95
}],
“lend”: [{
“user_id”: 3,
“amount”: 10
},
{
“user_id”: 4,
“amount”: 95
}],
}, ...
]
}
My output:
{
"results": [
{
"id": 1,
"category": 1,
"description": "lunch at the burj al arab",
"total_amount": 105,
"payment": [
{
"user": 1,
"amount": -10
},
{
"user": 2,
"amount": -95
},
{
"user": 3,
"amount": 10
},
{
"user": 4,
"amount": 95
}
]
}
]
}
My Serializer:
class ExpenseDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = Expense_Details
fields = ['user', 'amount']
class ExpenseSerializer(serializers.ModelSerializer):
payment = serializers.SerializerMethodField()
def get_payment(self, obj):
return ExpenseDetailsSerializer(obj.payment.all(), many=True).data
class Meta:
model = Expense
fields = ['id', 'category', 'description', 'total_amount', 'payment',]
What Query should I use to get Output in the above format? How will my serializer look like? How can I separate own and lend? Also I have stored own and lend with + and - sign to differentiate between them.
Use a ListField for the same.
Documentation: https://www.django-rest-framework.org/api-guide/fields/#listfield
Also refer How to serialize an 'object list' in Django REST Framework
Here you can try something like:
class ExpenseSerializer(serializers.Serializer):
payment = serializers.ListField(child=ExpenseDetailsSerializer())
def get_payment(self, obj):
return ExpenseDetailsSerializer(obj.payment.all(), many=True).data
class Meta:
model = Expense
fields = ['id', 'category', 'description', 'total_amount', 'payment',]

How can i display detailed data from many to many field instead of objects in django rest API , currently im getting only objects of it

I'm new to Django,i tryed to list all invoices from my model Invoices and im getting items as only its object, to make the code efficient i need to get the whole data of items in a single query how can i get the details of items along with the data instead of item objects
Here is what i have tryed
models.py
class ItemsInvoice(models.Model):
invoiceId = models.CharField(max_length=20)
product_Id = models.CharField(max_length=20)
item_price = models.CharField(max_length=20)
class Invoices(models.Model):
customer = models.CharField(max_length=10,null=True)
total_amount = models.CharField(max_length=12,null=True)
items = models.ManyToManyField(ItemsInvoice,related_name="item_invoice")
views.py
class InvoiceView(ListAPIView):
serializer_class = InvoiceSerializers
def get_queryset(self):
# queryset = Invoices.objects.prefetch_related('items').all().order_by('-id')
queryset = Invoices.objects.all().order_by('-id')
return queryset
serializers.py
class InvoiceSerializers(serializers.ModelSerializer):
class Meta:
model = Invoices
fields = '__all__'
if i run it on my postman, its response will be like this
API response what i have
[
{
"id": 69,
"customer": "4",
"total_amount": "25000",
"items": [
66,
67,
68
]
}
]
but actually i want my output like this , ie the item field should list all data inside in it
API response what i want
[
{
"id": 69,
"customer": "4",
"total_amount": "25000",
"items": [
{
"id": 66,
"invoiceId": "69",
"product_Id": "3",
"item_price": "300",
},
{
"id": 67,
"invoiceId": "69",
"product_Id": "4",
"item_price": "200",
},
{
"id": 68,
"invoiceId": "69",
"product_Id": "4",
"item_price": "200",
}
]
}
]
how can i achieve it by using django-orm or raw query
You must define ItemInvoiceSerializer and set many=true.
class ItemInvoiceSerializer(serializers.ModelSerializer):
class Meta:
model = ItemsInvoice
fields = '__all__'
class InvoiceSerializers(serializers.ModelSerializer):
items = ItemInvoiceSerializer(many=True) # i changed true to True
class Meta:
model = Invoices
fields = '__all__'

Change the order of fields in normal serializer response

Using Django REST framework I want to change the serializer response fields order dynamically by a list of given field names.
Output from DRF is the following:
{
"items": [
{
"name": "John",
"age": 25,
"id": 3
},
{
"name": "Sam",
"age": 20,
"id": 8
}
]
}
My filed order list is:
order_list = ['id', 'age', 'name']
What I want is:
{
"items": [
{
"id": 3,
"age": 25,
"name": "John"
},
{
"id": 8,
"age": 20,
"name": "Sam"
}
]
}
Serializer code:
class ItemSerializer(serializers.Serializer):
name = serializers.CharField()
id = serializers.IntegerField()
age = serializers.IntegerField()
To change the ordering, you will need to change the field definition in the serializer meta class.
class ItemSerializer(serializers.Serializer):
name = serializers.CharField()
id = serializers.IntegerField()
age = serializers.IntegerField()
class Meta(object):
fields = ( # this needs to be ordered properly
'id',
'age',
'name',
)
You can loop over the serializer data and order the fields using OrderedDict.
The data in the serializer is already using OrderedDict, but we can use it to further order accordingly.
from collections import OrderedDict
data = serializer.data
ordered_data = []
for item in data:
item_dict = OrderedDict()
item_dict['id'] = item['id']
item_dict['age'] = item['age']
item_dict['name'] = item['name']
ordered_data.append(item_dict)

django rest framework: include related model fields in same path

class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('id','product_id','sku', 'title','price','images')
class WishListSerializer(serializers.ModelSerializer):
product = ProductSerializer()
class Meta:
model = WishList
fields = ('wishlist_id','product',)
I have two serializers. Wishlist and Product. I want to list all wishlist products. It works fine now. But the product details is in "product" key element. Can I remove that product key, and show the product details along with the wishlist_id ?
Present result:
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"wishlist_id":1,
"product": {
"id": 1460,
"product_id": "04396134-3c90-ea7b-24ba-1fb0db11dbe5",
"sku": "bb4sd817",
"title": "Trinity Belt",
}
},
{
"wishlist_id":2,
"product": {
"id": 596,
"product_id": "52588d22-a62c-779b-8044-0f8d9892e853",
"sku": "ml346",
"title": "Martina Liana",
}
}
]
}
Expected result:
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"wishlist_id":1,
"id": 1460,
"product_id": "04396134-3c90-ea7b-24ba-1fb0db11dbe5",
"sku": "bb4sd817",
"title": "Trinity Belt",
},
{
"wishlist_id":2,
"id": 596,
"product_id": "52588d22-a62c-779b-8044-0f8d9892e853",
"sku": "ml346",
"title": "Martina Liana",
}
]
}
This is a very bad practice and you need a lot of effort to implement serialization and deserialization, especially in case of Post, Update etc.
I can think of 2 ways.
1) You could use in the WishListSerializer the missing fields as SerializerMethodField
example
product_id = serializers.SerializerMethodField()
def get_product_id(self, obj):
return obj.get_product().product_id
2)
class WishListSerializer(serializers.HyperlinkedModelSerializer):
product_id = serializers.CharField(source='product.product_id')
.......
class Meta:
model = WishList
fields = (product_id, ......)
You could "unnest" items in to_representation
docs on it https://www.django-rest-framework.org/api-guide/fields/#custom-fields
...
class WishListSerializer(serializers.ModelSerializer):
product = ProductSerializer()
class Meta:
model = WishList
fields = ('wishlist_id', 'product',)
# add
def to_representation(self, instance):
data = super().to_representation(instance)
flat_data = dict(wishlist_id=data['wishlist_id'], **data['product']) # construct (new dict) flat data
# or you could restructure same dict: data.update(data.pop('product'))
return flat_data