Add intermediate model fields to reverse relationship - django

I have two related models (Product and Recipe). There is a many-to-many relationship defined between them (with a through model). I am able to query the 'through' model fields using the below serializer, but I am unable to make this work the other way around (from Product to Recipe)
models.py:
class Product(models.Model):
version = models.CharField(max_length=4)
displayname = models.CharField(max_length=50,validators=[alphanumeric])
...
class Recipe(models.Model):
...
ingredients = models.ManyToManyField(Product, through='RecipeInput', related_name='recipe_input')
products = models.ManyToManyField(Product, through='RecipeOutput',related_name='recipe_output')
class RecipeInput(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
amount = models.IntegerField(default=1)
amount_min = models.FloatField(blank=True, null=True)
class RecipeOutput(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
amount = models.IntegerField(default=1)
amount_min = models.FloatField(blank=True, null=True)
mj = models.FloatField(blank=True, null=True)
serializers.py:
class RecipeInputSerializer(serializers.HyperlinkedModelSerializer):
product_id = serializers.ReadOnlyField(source='product.id')
product_name = serializers.ReadOnlyField(source='product.displayname')
class Meta:
model = RecipeInput
fields = ('product_id',"product_name", 'amount', 'amount_min', )
class RecipeOutputSerializer(serializers.HyperlinkedModelSerializer):
product_id = serializers.ReadOnlyField(source='product.id')
product_name = serializers.ReadOnlyField(source='product.displayname')
class Meta:
model = RecipeOutput
fields = ('product_id',"product_name", 'amount', 'amount_min', 'mj', )
class ProductInputSerializer(serializers.HyperlinkedModelSerializer):
recipe_id = serializers.ReadOnlyField(source='recipe.id')
recipe_name = serializers.ReadOnlyField(source='recipe.displayname')
class Meta:
model = RecipeInput
fields = ('recipe_id','recipe_name', 'amount_min', 'amount', )
class RecipeSerializer(serializers.ModelSerializer):
ingredients = RecipeInputSerializer(source='recipeinput_set', many=True)
products = RecipeOutputSerializer(source='recipeoutput_set', many=True)
class Meta:
model = Recipe
fields = "__all__"
depth = 1
class ProductSerializer(serializers.ModelSerializer):
products_in = ProductInputSerializer(source='recipe_input', many=True)
class Meta:
model = Product
fields = "__all__"
depth = 1
Now, this works for Recipe, I get the following output:
[
{
"id": 1239,
"ingredients": [
{
"product_id": 1787,
"product_name": "Automated Wiring",
"amount": 15,
"amount_min": 7.5
},
{
"product_id": 1799,
"product_name": "Circuit Board",
"amount": 10,
"amount_min": 5.0
}
],
"products": [
{
"product_name": "Adaptive Control Unit",
"amount": 2,
"amount_min": 1.0,
"mj": 3300.0
}
],
...
},
{
"id": 1240,
"ingredients": [
{
"product_id": 1809,
"product_name": "Copper Sheet",
"amount": 5,
"amount_min": 25.0
},
{
"product_id": 1871,
"product_name": "Quickwire",
"amount": 20,
"amount_min": 100.0
}
],
"products": [
{
"product_name": "AI Limiter",
"amount": 1,
"amount_min": 5.0,
"mj": 180.0
}
],
...
}
]
But, for Product, I am not able to query the related model (Recipe) and the values of the through model. In the serializers.py I defined products_in but it doesnt show recipe_id, recipe_name, etc.
[
{
"id": 2541,
"products_in": [
{
"amount_min": null
}
],
...
},
{
"id": 2542,
"products_in": [
{
"amount_min": null
},
{
"amount_min": null
}
],
...
}
]

Related

Getting repeated data white using django orm

im filtering that user whoes order_status is completed and who have listing_id 5001. But im getting output data repeated
Here is my Code:
models.py
class Users(models.Model):
name = models.CharField(max_length=100)
phone = models.CharField(max_length=20, blank=True, null=True)
'''
class Meta:
managed = False
db_table = 'users'
class UserOrder2(models.Model):
order_id = models.AutoField(primary_key=True)
order_status = models.CharField(max_length=30,default='None')
listing_id = models.CharField(max_length=250,default='None')
user_id = models.ForeignKey(Users, on_delete=models.CASCADE, db_column="user_id")
'''
class Meta:
managed = False
db_table = 'user_order'
class UserOrderProduct2(models.Model):
order_id = models.ForeignKey(UserOrder2, on_delete=models.CASCADE, db_column="order_id")
product_name = models.CharField(max_length=100)
...
class Meta:
managed = False
db_table = 'user_order_product'
Views.py
class UserPurchaseQuantityView(generics.GenericAPIView):
def post(self, request):
listing_id= request.data.get('listing_id')
kwargs = {}
kwargs['userorder2__listing_id'] = listing_id
kwargs['userorder2__order_status'] = 'Order Delivered'
queryset = Users.objects.filter(**kwargs)
data = UsersSerializer(queryset, many=True).data
return Response(data)
serializers.py
class UserOrderProductSerializer2(serializers.ModelSerializer):
class Meta:
fields = ["product_id", "product_quantity", "product_price", "sub_total",
"product_name"]
model = UserOrderProduct2
class UserOrderSerializer(serializers.ModelSerializer):
product_detail = UserOrderProductSerializer2(source="userorderproduct2_set", many=True)
class Meta:
fields = ["user_id", "order_date", "product_detail"]
model = UserOrder2
class UsersSerializer(serializers.ModelSerializer):
user_detail = UserOrderSerializer(source="userorder2_set", many=True)
class Meta:
fields = "__all__"
model = Users
I'm getting repeated output like this:
[
{
"id": 51238,
"name": "aaa",
"phone": "123456789",
"email": "aaa#gmail.com",
"user_detail": [
{
"user_id": 51238,
"order_date": "2021-07-27 15:55:56"
"product_detail": [
{
"product_id": 20767,
"product_quantity": 1,
"product_price": 150.0,
"sub_total": 150.0,
"product_name": "EMINAZ 2mg Tablet 10's"
]
},
{
"id": 51238,
"name": "aaa",
"phone": "123456789",
"email": "aaa#gmail.com",
"user_detail": [
{
"user_id": 51238,
"order_date": "2021-07-27 15:55:56"
"product_detail": [
{
"product_id": 20767,
"product_quantity": 1,
"product_price": 150.0,
"sub_total": 150.0,
"product_name": "EMINAZ 2mg Tablet 10's"
] },
{
"id": 51238,
"name": "aaa",
"phone": "123456789",
"email": "aaa#gmail.com",
"user_detail": [
{
"user_id": 51238,
"order_date": "2021-07-27 15:55:56"
"product_detail": [
{
"product_id": 20767,
"product_quantity": 1,
"product_price": 150.0,
"sub_total": 150.0,
"product_name": "EMINAZ 2mg Tablet 10's"
] } ]
I think the issue is in **kwargs. Try this
class UserPurchaseQuantityView(generics.GenericAPIView):
def post(self, request):
listing_id= request.data.get('listing_id')
queryset = Users.objects.filter(userorder2__listing_id=listing_id,
userorder2__order_status='Order Delivered')
data = UsersSerializer(queryset, many=True).data
return Response(data)
Add distinct() to make unique.
queryset = Users.objects.filter(**kwargs).distinct()

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

category subcategory not showing properly drf django

i have this category models:
class Category(MPTTModel):
name = models.CharField(max_length = 100)
slug = models.SlugField()
parent = TreeForeignKey('self',blank=True, null=True, related_name='children', on_delete=models.CASCADE, db_index = True)
class MPTTMeta:
unique_together = ('slug', 'parent')
verbose_name_plural = "categories"
order_insertion_by = ['name']
def __str__(self):
full_path = [self.name]
k = self.parent
while k is not None:
full_path.append(k.name)
k = k.parent
return ' -> '.join(full_path[::-1])
serializers.py:
category Listing Api
class CategorySerializers(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('id','name','slug','children')
def get_fields(self):
fields = super().get_fields()
fields['children'] = CategorySerializer(many=True, read_only=True)
return fields
views.py:
class CategoryView(generics.ListCreateAPIView):
queryset = Category.objects.filter(parent__isnull=True)
serializer_class = CategorySerializers
currently its working but i want to show subcategory inside category only not as a main category as you can see subcategory are listed inside main category and with maincategory also
I am expecting this result:
[
{
"id": 1,
"name": "Animals & Wildlife",
"slug": "animals_wildlife",
"children": []
},
{
"id": 2,
"name": "main cat",
"slug": "mai_cat",
"children": [
{
"id": 3,
"name": "sub_cat",
"slug": "submaincat",
"children": []
}
]
},
{
"id": 4,
"name": "maincat2",
"slug": "maincat2",
"children": [
{
"id": 5,
"name": "submaincat2",
"slug": "submaincat2",
"children": []
}
]
},
{
"id": 6,
"name": "maincat3",
"slug": "maincat3",
"children": []
}
]
...............................................
Change your viewset to
class CategoryViewSet(ModelViewSet):
queryset = Category.objects.filter(parent__isnull=True)
serializer_class = CategorySerializer
and serializer to
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('id', 'name', 'slug')
def get_fields(self):
fields = super().get_fields()
fields['children'] = CategorySerializer(many=True, read_only=True)
return fields

How to count the distinct of model_set in Django?

I've looked into this question, but I think it's different.
Let me explain a bit further. I have a serializer called DetailTrackSerializer to serialize my Track model, and I've nested a TaggedSerializer in DetailTrackSerializer.
class DetailTrackSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=120)
link = serializers.URLField(max_length=120)
tagged_set = TaggedSerializer(many=True)
artist = ArtistSerializer()
class Meta:
model = Track
fields = ('id', 'artist', 'title', 'link', 'tagged_set',)
class TaggedSerializer(serializers.ModelSerializer):
tag = TagSerializer()
class Meta:
model = Tagged
fields = ('tag',)
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ('name',)
Currently, this DetailTrackSerializer is returning a json like this
{
"tracks": [
{
"id": 168,
"artist": {
"id": 163,
"name": "Gob"
},
"title": "Face the Ashes",
"link": "",
"tagged_set": [
{
"tag": {
"id": 1356,
"name": "punk rock"
}
},
{
"tag": {
"id": 1356,
"name": "punk rock"
}
},
{
"tag": {
"id": 1356,
"name": "punk rock"
}
},
...
The list goes on, if there are 100 "punk rock" tag in this track, it will shows up 100 times and there may be another tag also not only "punk rock". What I need is something like this
{
"tracks": [
{
"id": 168,
"artist": {
"id": 163,
"name": "Gob"
},
"title": "Face the Ashes",
"link": "",
"tagged_set": [
{
"tag": {
"id": 1356,
"name": "punk rock"
},
"frequency": 100,
},
{
"tag": {
"id": 546,
"name": "pop"
},
"frequency": 236,
},
...
Each tag only appears once, and has its frequency.
Note: I'm using Django Rest Framework as well
Edit: models.py
class Tagged(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
track = models.ForeignKey(Track, on_delete=models.CASCADE)
tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
class Tag(models.Model):
name = models.CharField(max_length=255, unique=True)
class Track(models.Model):
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
link = models.URLField(max_length=255, blank=True)
tags = models.ManyToManyField(Tag, through='Tagged', blank=True)
From your Tagged I understood that there are big chances of Data Redundancy, that's why your tagged_set is showing multiple times.
What I'm trying to say is, this is not a Representation Problem with your serializer, rather than it's an Implementation Problem with your Models.
So, unique_together attribute will solve the problem, as
class Tagged(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
track = models.ForeignKey(Track, on_delete=models.CASCADE)
tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
class Meta:
unique_together = ('track', 'tag')
After changing the models, please do makemigrations and migration.
Note: While doing migration you may come acrross django.db.utils.IntegrityError: UNIQUE constraint failed exception. So, delete all entries in the Tagged model
After reading through Django's docs about querysets, this is the solution I came up with
class DetailTrackSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=120)
link = serializers.URLField(max_length=120)
tags_frequency = serializers.SerializerMethodField()
artist = ArtistSerializer()
def get_tags_frequency(self, track):
tags = track.tags.all()
return tags.values('id', 'name').annotate(Count('id'))
class Meta:
model = Track
fields = ('id', 'artist', 'title', 'link', 'tags_frequency',)
which will give me json representation like this
{
"tracks": [
{
"id": 168,
"artist": {
"id": 163,
"name": "Gob"
},
"title": "Face the Ashes",
"link": "",
"tags_frequency": [
{
"name": "punk rock",
"id": 1356,
"id__count": 100
},
{
"name": "punk",
"id": 1357,
"id__count": 60
}
]
},
{
"id": 169,
"artist": {
"id": 164,
"name": "Jeff And Sheri Easter"
},
"title": "The Moon And I (Ordinary Day Album Version)",
"link": "",
"tags_frequency": []
},
Edwin Harly, you have some data overlap between Track, Tag and Tagged model. If you accept, I suggest you remove Tagged model. and if you want to save which user create tag, add user field in Tag model.
class Tag(models.Model):
name = models.CharField(max_length=255, unique=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
class Track(models.Model):
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
link = models.URLField(max_length=255, blank=True)
tags = models.ManyToManyField(Tag, through='Tagged', blank=True)
Then, you can your serializers like this:
class DetailTrackSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=120)
link = serializers.URLField(max_length=120)
tags = TagSerializer(many=True)
artist = ArtistSerializer()
class Meta:
model = Track
fields = ('id', 'artist', 'title', 'link', 'tags',)
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ('name',)

How can i serialize manytomany django models using DRF

How can i get products name and id instead of pro_id and ord_id in output ? but not in string type. for example : "name : somebook" is not a valid option for me.
I just working on this for 2 days without break and i think im missing a little detail but i cant find what is it.
Output i want
[
{
"order_id": 1,
"totalquantity": 12,
"totalprice": 56,
"userid": 1,
"customerAddress": "evka1",
"customerPhone": "539",
"trackNo": 12034,
"products": [
{
"name": "somebook",
"id": 1,
"quantity": 6
},
{
"name": "someotherbook",
"id": 2,
"quantity": 6
}
]
}
]
Output i get
[
{
"order_id": 1,
"totalquantity": 12,
"totalprice": 56,
"userid": 1,
"customerAddress": "evka1",
"customerPhone": "539",
"trackNo": 12034,
"products": [
{
"pro_id": 2,
"ord_id": 1,
"quantity": 6
},
{
"pro_id": 3,
"ord_id": 1,
"quantity": 6
}
]
}
]
Order model
class Order(models.Model):
order_id = models.AutoField(primary_key=True)
totalquantity = models.IntegerField(default=0, null=True)
totalprice = models.IntegerField(default=0, null=True)
userid = models.IntegerField(default=0, null=True)
trackNo = models.IntegerField(default=0, null=True)
billNo = models.IntegerField(default=0, null=True)
customerAddress = models.CharField(max_length=30,default="nil", null=True)
customerPhone = models.CharField(max_length=30,default="nil", null=True)
Order Serializer
class OrderSerializer(serializers.ModelSerializer):
products = ProductOrderSerializer(many=True, read_only=True)
class Meta:
model = Order
fields = ('order_id', 'totalquantity', 'totalprice', 'userid', 'customerAddress', 'customerPhone', 'trackNo', 'products')
Product Model
class Product(models.Model):
name = models.CharField(max_length=30,default="nil", null=True)
author = models.CharField(max_length=30,default="nil", null=True)
date = models.DateField(null=True)
price = models.FloatField(default=0, null=True)
quantity = models.IntegerField(default=0, null=True)
soldcount = models.IntegerField(default=0, null=True)
category = models.CharField(max_length=30,default="nil", null=True)
Product Serializer
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('id', 'name', 'author', 'date', 'price', 'quantity', 'soldcount', 'category')
ProductOrder Model
class ProductOrder(models.Model):
pro_id = models.IntegerField(default=0,null=True)
ord_id = models.ForeignKey(Order, related_name='products')
quantity = models.IntegerField(default=0, null=True)
ProductOrder Serializer
class ProductOrderSerializer(serializers.ModelSerializer):
class Meta:
model = ProductOrder
fields = ('pro_id', 'ord_id', 'quantity')
The output you desired can be achieved with nested serializers in django-rest-framework,
But, it needs some refactoring in your models,
You need to add a ForeignKey to the model ProductOrder towards the Product model, inorder to attain a ManyToMany relation,
class ProductOrder(models.Model):
pro_id = models.ForeinKey(Product, related_name='orders', default=0,null=True)
ord_id = models.ForeignKey(Order, related_name='products')
quantity = models.IntegerField(default=0, null=True)
Also, in your serializers,
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('name', 'id', 'quantity')
class ProductOrderSerializer(serializers.ModelSerializer):
details = ProductSerializer(source='pro_id', read_only=True)
class Meta:
model = ProductOrder
fields = ('details', )
class OrderSerializer(serializers.ModelSerializer):
products = ProductOrderSerializer(many=True, read_only=True)
class Meta:
model = Order
fields = ('totalquantity', 'totalprice', 'userid',
'trackNo', 'billNo', 'customerAddress', 'customerPhone',
'products')
You could call OrderSerializer and get the output as you desired,
def get(self, request, *args, **kwargs):
orders = Order.objects.all()
serializer = OrderSerializer(orders, many=True)
return Response(serializer.data)
The output will be like this,
[
{
"totalquantity": 0,
"totalprice": 0,
"userid": 0,
"trackNo": 0,
"billNo": 0,
"customerAddress": "nil",
"customerPhone": "nil",
"products": [
{
"details": {
"name": "nil",
"id": 1,
"quantity": 0
}
},
{
"details": {
"name": "nil",
"id": 1,
"quantity": 0
}
}
]
}
]
I solved the problem when i changed ProductOrderSerializer to :
class ProductListingSerializer(serializers.RelatedField):
def to_representation(self, value):
dict={}
dict['name'] = value.pro_id.name
dict['id'] = value.pro_id.pk
dict['quantity'] = value.quantity
return dict
but im in "my code works i don't know why" situation now :D