How to remove duplicate nested objects Django REST? - django

How to remove duplicate nested comments Django REST?
Serializer recoursive displays comments.
In JSON duplicates comments (the third is displayed in two places):
How to get rid of it?
{
"id": 1,
"slug": "mp3kit",
"publications_date": "2019-03-19T13:49:58Z",
"tag": "Tag",
"title": "Title",
"content_preview": "Content preview",
"body": "Body",
"image_preview": null,
"image_preview_name": "mp3kit",
"comments": [
{
"name": "First",
"body": "First",
"date": "2019-06-02T15:09:16.788405Z",
"id": "57fd0069-a8ce-484f-afac-4cc609a1b70c",
"parent": null,
"reply": [
{
"name": "Third",
"body": "Third",
"date": "2019-06-02T15:09:42.512872Z",
"id": "0801ef96-796d-477b-8631-b21559527156",
"parent": "57fd0069-a8ce-484f-afac-4cc609a1b70c",
"reply": []
}
]
},
{
"name": "Second",
"body": "Second",
"date": "2019-06-02T15:09:27.209497Z",
"id": "5b2f0d25-bcd0-4b19-b05c-e6a5e7de1280",
"parent": null,
"reply": []
},
{
"name": "Third",
"body": "Third",
"date": "2019-06-02T15:09:42.512872Z",
"id": "0801ef96-796d-477b-8631-b21559527156",
"parent": "57fd0069-a8ce-484f-afac-4cc609a1b70c",
"reply": []
}
]
}
As I understand you need to put a filter on the field "comment" in ArticleSerializer, to disable output comments with parent?
Model:
class Article(models.Model):
id = models.AutoField(primary_key=True)
slug = models.SlugField()
publications_date = models.DateTimeField(blank=True, null=True, default=datetime.datetime.now)
tag = models.CharField(max_length=100, blank=True, null=True)
title = models.CharField(max_length=100, blank=True, null=True)
content_preview = models.TextField(blank=True)
body = HTMLField(blank=True, null=True)
image_preview = models.ImageField(upload_to=upload_to, blank=True)
image_preview_name = models.CharField(max_length=100, blank=True, null=True)
def __str__(self):
return self.title
#property
def comments_list(self):
return self.comments.filter()
class Comment(models.Model):
id = models.CharField(max_length=100, blank=True, unique=True, default=uuid.uuid4, primary_key=True)
date = models.DateTimeField(auto_now=True, auto_now_add=False)
name = models.CharField(max_length=100)
body = models.TextField(blank=True, null=True)
post = models.ForeignKey(Article, on_delete=models.CASCADE, blank=True,
related_name='comments', related_query_name='comment')
parent = models.ForeignKey('self', related_name='reply', null=True, blank=True, on_delete=models.PROTECT)
def __str__(self):
return f'Post - "{self.post.title}", Body - "{self.body}"'
Serializer:
class RecursiveSerializer(serializers.Serializer):
def to_representation(self, value):
serializer = self.parent.parent.__class__(value, context=self.context)
return serializer.data
class CommentSerializer(serializers.ModelSerializer):
"""DRF Serializer For Listing Published Comment"""
reply = RecursiveSerializer(many=True, read_only=True)
class Meta:
model = Comment
fields = [
'name',
'body',
'date',
'id',
'parent',
'reply'
]
class ArticleSerializer(serializers.ModelSerializer):
comments = CommentSerializer(many=True)
class Meta:
model = Article
lookup_field = 'slug'
fields = (
'id',
'slug',
'publications_date',
'tag',
'title',
'content_preview',
'body',
'image_preview',
'image_preview_name',
'comments'
)
Views:
class DetailArticle(generics.RetrieveUpdateDestroyAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
lookup_field = 'slug'

You can achieve that by using a SerializerMethodField inside your ArticleSerializer, like this:
class ArticleSerializer(serializers.ModelSerializer):
comments = serializers.SerializerMethodField()
def get_comments(self, instance):
return CommentSerializer(instance.comments.filter(parent__isnull=True), many=True).data
class Meta:
model = Article
lookup_field = 'slug'
fields = (
'id',
'slug',
'publications_date',
'tag',
'title',
'content_preview',
'body',
'image_preview',
'image_preview_name',
'comments'
)

Related

Django - Too many similar queries

I'm creating a music rating app and I'm making a serializer for albums which has many relations and one aggregation method serializer which I think is causing all the trouble. The method gets average and count of reviews for every album. Is there any way I can decrease the number of queries for more performance?
All my models
class Artist(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255)
image = models.FileField(null=True, blank=True,
upload_to=rename_artist_image)
background_image = models.FileField(
null=True, blank=True, upload_to=rename_artist_bg_image)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self) -> str:
return self.name
class Album(models.Model):
RELEASE_TYPE_ALBUM_CHOICES = [
("LP", "LP"),
("EP", "EP"),
("Single", "Single"),
("Live", "Live"),
]
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=255)
release_date = models.DateField(null=True)
artist_id = models.ForeignKey(
Artist, on_delete=models.PROTECT, related_name="albums"
)
art_cover = models.FileField(
null=True, blank=True, upload_to=rename_album_art_cover)
release_type = models.CharField(max_length=10,
choices=RELEASE_TYPE_ALBUM_CHOICES, default="LP")
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self) -> str:
return self.title
class Genre(models.Model):
name = models.CharField(max_length=255)
def __str__(self) -> str:
return self.name
class AlbumGenre(models.Model):
album_id = models.ForeignKey(
Album, on_delete=models.PROTECT, related_name="album_genres")
genre_id = models.ForeignKey(
Genre, on_delete=models.PROTECT, related_name="album_genres")
def __str__(self) -> str:
return self.genre_id.name
class AlbumLink(models.Model):
SERVICE_NAME_CHOICES = [
("spotify", "Spotify"),
("tidal", "Tidal"),
("amazonMusic", "Amazon Music"),
("appleMusic", "Apple Music"),
]
service_name = models.CharField(
max_length=15, choices=SERVICE_NAME_CHOICES)
url = models.CharField(max_length=255)
album_id = models.ForeignKey(
Album, on_delete=models.PROTECT, related_name="album_links")
def __str__(self) -> str:
return f"{self.service_name} - {self.url}"
class Track(models.Model):
title = models.CharField(max_length=255)
position = models.PositiveIntegerField()
album_id = models.ForeignKey(
Album, on_delete=models.PROTECT, related_name="tracks")
duration = models.DurationField(null=True)
def __str__(self) -> str:
return f"{self.position}. {self.title} - {self.duration}"
class AlbumOfTheYear(models.Model):
album_id = models.OneToOneField(
Album, on_delete=models.PROTECT, related_name="aoty")
position = models.IntegerField()
def __str__(self) -> str:
return str(self.position)
class Review(models.Model):
reviewer_id = models.ForeignKey(Reviewer, on_delete=models.PROTECT)
rating = models.IntegerField(
validators=[MaxValueValidator(100), MinValueValidator(0)]
)
review_text = models.TextField(null=True)
album_id = models.ForeignKey(
Album, on_delete=models.PROTECT, related_name="reviews")
created_at = models.DateTimeField(auto_now_add=True)
That's how album serializer looks.
"id": 2,
"title": "OK Computer",
"slug": "ok-computer",
"created_at": "2022-02-22T21:51:52.528148Z",
"artist": {
"id": 13,
"name": "Radiohead",
"slug": "radiohead",
"image": "http://127.0.0.1:8000/media/artist/images/radiohead.jpg",
"background_image": "http://127.0.0.1:8000/media/artist/bg_images/radiohead.jpg",
"created_at": "2022-02-22T00:00:00Z"
},
"art_cover": "http://127.0.0.1:8000/media/album/art_covers/ok-computer_cd5Vv6U.jpg",
"genres": [
"Alternative Rock",
"Art Rock"
],
"reviews": {
"overall_score": null,
"number_of_ratings": 0
},
"release_date": "1997-05-28",
"release_type": "LP",
"tracks": [
{
"position": 1,
"title": "Airbag",
"duration": "00:04:47"
},
{
"position": 2,
"title": "Paranoid Android",
"duration": "00:06:27"
}
],
"links": [
{
"service_name": "spotify",
"url": "https://open.spotify.com/album/6dVIqQ8qmQ5GBnJ9shOYGE?si=L_VNH3HeSMmGBqfiqKiGWA"
}
],
"aoty": null
Album serializer with method that gets average and count of reviews of album
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True, read_only=True)
genres = StringRelatedField(
source="album_genres", many=True, read_only=True)
aoty = StringRelatedField(read_only=True)
links = AlbumLinkSerializer(
source="album_links", many=True, read_only=True)
artist = SimpleArtistSerializer(source="artist_id")
def get_avg_and_count_of_reviews(self, album: Album):
reviews = Review.objects.only("rating").filter(album_id=album.id).aggregate(
overall_score=Avg(F("rating"), output_field=IntegerField()), number_of_ratings=Count(F("rating"), output_field=IntegerField()))
return reviews
reviews = serializers.SerializerMethodField(
method_name="get_avg_and_count_of_reviews")
class Meta:
model = Album
fields = ["id",
"title",
"slug",
"created_at",
"artist",
"art_cover",
"genres",
"reviews",
"release_date",
"release_type",
"tracks",
"links",
"aoty"]
# Save slug
def create(self, validated_data):
slug = slugify(validated_data["title"])
return Album.objects.create(slug=slug, **validated_data)
Here is a queryset in album Viewset
class AlbumViewSet(ModelViewSet):
queryset = Album.objects.prefetch_related("tracks").prefetch_related("album_genres").prefetch_related(
"album_links").prefetch_related("reviews").select_related("aoty").select_related("artist_id").all()
First you need to change your aggregate that you call once for every Album to an annotation, this will remove all of those extra aggregation queries
class AlbumViewSet(ModelViewSet):
queryset = Album.objects.prefetch_related(
"tracks",
"album_genres",
"album_links",
"reviews"
).select_related(
"aoty",
"artist_id"
).annotate(
overall_score=Avg(F("reviews__rating"), output_field=IntegerField()),
number_of_ratings=Count(F("reviews__rating"), output_field=IntegerField())
)
Now you can replace your reviews field with two regular IntegerFields
class AlbumSerializer(serializers.ModelSerializer):
...
overall_score = serializers.IntegerField(source="overall_score")
number_of_ratings = serializers.IntegerField(source="number_of_ratings")

How to add model instances to a Foreign Key field in Django Rest Framework

I need to get the ItemModel instances from the item model and store them in the foreign key field in the OrderModel but I am not sure how. I've tried to iterate through the item order list and add one but it doesn't display correctly. Any help would be much appreciated.
My goal is to display the data like this:
{
"payment_method": "test",
"confirmation": "14087147WA285750M",
"total_price": "15.00",
"is_paid": "True",
"order_created": "2021-07-09T19:51:18Z",
"item_order": [
{
"id": 2,
"name": "Carrots",
"image": "image link",
"slug": "carrots",
"price": "5.00",
"itemID": "ct1",
"quantity": 1
},
{
"id": 8,
"name": "Dog Food",
"image": "image link",
"slug": "dog-food",
"price": "10.00",
"itemID": "df4",
"quantity": 1
}
]
}
View:
#api_view(['POST'])
def create_order(request):
user = request.user
order = request.data
order_item = OrderModel.objects.create(
user=user,
payment_method=order['payment_method'],
confirmation=order['confirmation'],
total_price=order['total_price'],
is_paid=order['is_paid'],
order_created=order['order_created'],
item_order= """ The item model instance """
)
ordered_items = OrderSerializer(order_item, many=True).data
return Response(ordered_items)
Order Model:
class OrderModel(models.Model):
user = models.ForeignKey(CustomerModel, on_delete=models.CASCADE)
item_order = models.ForeignKey(
ItemModel, on_delete=models.CASCADE)
payment_method = models.CharField(max_length=50)
confirmation = models.CharField(max_length=255)
total_price = models.DecimalField(
max_digits=5, decimal_places=2)
is_paid = models.BooleanField(default=False)
has_been_sent = models.BooleanField(default=False)
order_created = models.DateTimeField()
def __str__(self):
return str(self.id)
Order Serializer:
class OrderSerializer(serializers.ModelSerializer):
item_order = ItemSerializer(many=True)
class Meta:
model = OrderModel
fields = ['payment_method', 'confirmation',
'total_price', 'is_paid', 'has_been_sent', 'order_created',
'item_order']
read_only_fields = ['id']

Can't represent all fields of a serialized ManyToMany through model with Django Rest Framework

I'm using Django and Django Rest Framework to represent a 'BaseRecipe' model. This model has a M2M field to represent the ingredients of that recipe. That M2M field relates a 'Product' object with the 'BaseRecipe' object and extends that two models with another field to represent the 'quantity'. I'm trying to retrieve a list of those ingredients in the BaseRecipeSerializer, but only the id's are returned.
Any ideas why?
Thank you in advance!
My models.py:
class Product(models.Model):
name = models.CharField(_('Name'), max_length=255, help_text=_('Product name'))
supplier = models.ForeignKey(Supplier, blank=True, related_name='supplier_products', on_delete=models.CASCADE)
serial_no = models.CharField(_('Serial number'), max_length=50, blank=True,
help_text=_('Product serial number. Max 50 characters.'))
allergens = models.ManyToManyField(Allergen, blank=True, related_name='product_allergens')
description = models.TextField(_('Description'), blank=True, help_text=_('Additional product information.'))
is_vegan = models.BooleanField(_('Vegan'), default=False)
is_halal = models.BooleanField(_('Halal'), default=False)
format_container = models.CharField(_('Format container'), max_length=6, choices=CONTAINERS, blank=True,
help_text=_('Package format'))
format_quantity = models.DecimalField(_('Format quantity'), blank=True, null=True, max_digits=9, decimal_places=3,
help_text=_('Format quantity sell'))
format_unit = models.CharField(_('Format unit'), max_length=6, choices=UNITS, blank=True)
quantity = models.DecimalField(_('Quantity'), blank=True, null=True, max_digits=9, decimal_places=3,
help_text=_('Quantity per unit provided'))
type = models.CharField(_('Type'), max_length=255, help_text=_('Type'), blank=True)
unit = models.CharField(_('Unit'), max_length=6, choices=UNITS)
unit_price = models.DecimalField(_('Unit price'), blank=True, null=True, max_digits=9, decimal_places=3)
class Meta:
ordering = ['name', ]
#property
def price(self) -> Decimal:
return self.quantity * self.unit_price
def __str__(self):
return self.name
class BaseRecipe(models.Model):
user = models.ForeignKey(User, null=True, on_delete=models.CASCADE, related_name='user_base_recipes')
restaurant = models.ForeignKey(Restaurant, null=True, on_delete=models.SET_NULL,
related_name='restaurant_base_recipes')
title = models.CharField(_('Base recipe title'), max_length=255, help_text=_('Base recipe. Example: Chicken broth'),
blank=True)
elaboration = models.TextField(_('Elaboration'), blank=True, help_text=_('Base recipe making instructions.'))
quantity = models.DecimalField(_('Quantity'), max_digits=9, decimal_places=3,
help_text=_('Quantity produced with this recipe (after cooking). '))
unit = models.CharField(_('Unit'), max_length=6, choices=UNITS)
expiry_date = models.DateField(_('Expiry date'), help_text=_('Last day product is safe to consume.'))
ingredients = models.ManyToManyField(Product, through='IngredientBaseRecipe', related_name='base_recipe_ingredients')
image = models.ImageField(_('Picture'), upload_to=get_picture_path, blank=True, null=True)
exhausted = models.BooleanField(_('Exhausted'), default=False, help_text=_('If it\'s needed to produce more.'))
next_batch_date = models.DateField(_('Next batch date'), blank=True,
help_text=_('When it\'s necessary to prepare more of this base recipe.'))
storage = models.CharField(_('Storage'), max_length=12, choices=STORAGE_METHODS, blank=True)
class Meta:
ordering = ['-id']
#property
def cost(self) -> Decimal:
return sum([ingredient.cost for ingredient in self.ingredients.all()])
#property
def allergens(self):
allergens_ids = IngredientBaseRecipe.objects.filter(base_recipe=self).values_list('product__allergens',
flat=True)
allergens = Allergen.objects.filter(id__in=allergens_ids)
return allergens
def __str__(self):
return self.title
class IngredientBaseRecipe(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
base_recipe = models.ForeignKey(BaseRecipe, on_delete=models.CASCADE)
product_quantity = models.DecimalField(_('Product quantity'), max_digits=9, decimal_places=3, default=0.0)
class Meta:
ordering = ['-id']
#property
def allergens(self):
return self.product.allergens.all()
#property
def cost(self) -> Decimal:
return self.quantity * self.product.unit_price
def __str__(self):
return self.product.name
serializer.py:
class SimpleProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ['id', 'name', ]
class IngredientBaseRecipeSerializer(serializers.ModelSerializer):
# product_name = serializers.ReadOnlyField(source='product.name')
product = SimpleProductSerializer(read_only=True)
class Meta:
model = IngredientBaseRecipe
exclude = ['base_recipe', ]
class BaseRecipeSerializer(serializers.ModelSerializer):
ingredients = IngredientBaseRecipeSerializer(many=True, read_only=True)
allergens = AllergenSerializer(many=True, read_only=True)
cost = serializers.FloatField(read_only=True)
class Meta:
model = BaseRecipe
fields = '__all__'
Result:
{
"count": 3,
"next": null,
"previous": null,
"results": [
{
"id": 5,
"ingredients": [
{
"id": 1
},
{
"id": 3
},
{
"id": 3
}
],
"allergens": [
{
"id": 9,
"name": "Celery",
"icon": null
},
{
"id": 12,
"name": "Sulphites",
"icon": null
}
],
"title": "Tipical Spanish",
"elaboration": "No se tio.",
"quantity": "23232.000",
"unit": "l",
"expiry_date": "2021-02-28",
"image": null,
"exhausted": false,
"next_batch_date": "2021-03-01",
"storage": "freezer",
"user": 1,
"restaurant": 1
},
{
"id": 3,
"ingredients": [],
"allergens": [],
"cost": 0.0,
"title": "Chicken broth",
"elaboration": "One, two, three.",
"quantity": "45.000",
"unit": "l",
"expiry_date": "2021-02-28",
"image": null,
"exhausted": false,
"next_batch_date": "2021-03-01",
"storage": "freezer",
"user": 1,
"restaurant": 1
}
]
}
BaseRecipe.ingredients will return a queryset of Product instances but you are passing them to the IngredientBaseRecipeSerializer. You need to change the source for this field so that you pass the related IngredientBaseRecipe instances to this field
class BaseRecipeSerializer(serializers.ModelSerializer):
ingredients = IngredientBaseRecipeSerializer(
many=True,
read_only=True,
source='ingredientbaserecipe_set'
)
...

Override 'create' function in Django serializer

I wonder how to handle POST request to properly save the incoming data, having such models:
class Recipe(models.Model):
author = models.ForeignKey('auth.user', related_name='recipes', on_delete=models.CASCADE)
image = models.TextField(default='None')
name = models.CharField(max_length=100)
description = models.TextField(default='No description')
votes = models.IntegerField(default=0)
def __str__(self):
return self.name
class Ingredient(models.Model):
image = models.TextField(default='None')
name = models.CharField(max_length=100)
description = models.TextField(default='No description')
price = models.DecimalField(max_digits=8, decimal_places=3)
unit_price = models.DecimalField(max_digits=8, decimal_places=3)
unit_quantity = models.CharField(max_length=20)
def __str__(self):
return self.name
I wanted to avoid duplicating Ingredient objects, so to provide quantity of specific Ingredient in Recipe I've created a RecipesIngredient model that binds Ingredient with Recipe, but also contains a quantity of this Ingredient:
class RecipesIngredient(models.Model):
recipe = models.ForeignKey(Recipe, related_name='ingredients', on_delete=models.CASCADE)
ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE)
quantity = models.CharField(max_length=100)
def __str__(self):
return self.quantity
I've also prepared some serializers for these models:
class IngredientSerializer(HyperlinkedModelSerializer):
class Meta:
model = Ingredient
fields = (
'url',
'image',
'name',
'description',
'price',
'unit_price',
'unit_quantity'
)
class RecipesIngredientSerializer(HyperlinkedModelSerializer):
ingredient_name = ReadOnlyField(source='ingredient.name')
ingredient_price = ReadOnlyField(source='ingredient.price')
ingredient_unit_price = ReadOnlyField(source='ingredient.unit_price')
ingredient_unit_quantity = ReadOnlyField(source='ingredient.unit_quantity')
class Meta:
model = RecipesIngredient
fields = (
'url',
'ingredient_name',
'quantity',
'ingredient_price',
'ingredient_unit_price',
'ingredient_unit_quantity'
)
class RecipeListSerializer(HyperlinkedModelSerializer):
author = ReadOnlyField(source='author.username')
author_url = ReadOnlyField(source='author.url')
class Meta:
model = Recipe
fields = (
'url',
'author',
'author_url',
'image',
'name',
'description',
'votes'
)
class RecipeDetailSerializer(HyperlinkedModelSerializer):
author = ReadOnlyField(source='author.username')
author_url = ReadOnlyField(source='author.url')
ingredients = RecipesIngredientSerializer(many=True)
class Meta:
model = Recipe
fields = (
'url',
'author',
'author_url',
'image',
'name',
'description',
'ingredients',
'votes'
)
But in this case, I have to first create a Recipe instance and save it to DB, then do the same with Ingredient to be able to "bind" them in RecipesIngredient. Is this possible to handle this case with only one POST request to view below?
#
# path('recipes/', views.RecipeList.as_view(), name='recipe-list')
#
class RecipeList(generics.ListCreateAPIView):
queryset = Recipe.objects.all()
serializer_class = RecipeListSerializer
def perform_create(self, serializer):
serializer.save(author=self.request.user)
#EDIT
I forgot 'bout this thread, but the problem is solved now. I've prepared another serializer for CREATE purposes only, and overridden the 'create' function of this serializer.:
class RecipeCreateSerializer(HyperlinkedModelSerializer):
#author = ReadOnlyField(source='author.username')
#author_url = ReadOnlyField(source='author.url')
recipes_ingredients = RecipesIngredientCreateSerializer(many=True)
def create(self, validated_data):
recipes_ingredients = validated_data.pop('recipes_ingredients')
recipe_instance = super().create(validated_data)
for recipe_ingredient in recipes_ingredients:
ingredient_data = recipe_ingredient.pop('ingredient')
ingredient_instance = Ingredient(
image=ingredient_data['image'],
name=ingredient_data['name'],
description=ingredient_data['description'],
price=ingredient_data['price'],
unit_price=ingredient_data['unit_price'],
unit_quantity=ingredient_data['unit_quantity'],
)
ingredient_instance.save()
recipes_ingredient_instance = RecipesIngredient(
recipe=recipe_instance,
ingredient=ingredient_instance,
quantity=recipe_ingredient['quantity']
)
recipes_ingredient_instance.save()
return recipe_instance
class Meta:
model = Recipe
fields = (
'url',
'image',
'name',
'description',
'votes',
'recipes_ingredients',
)
Also the JSON file looks a bit different now, but everything works just fine:
{
"image": "image-url",
"name": "recipes-name",
"description": "recipes-description",
"votes": 0,
"recipes_ingredients": [
{
"quantity": "ingredients-quantity",
"ingredient": {
"image": "image-url",
"name": ingredient-name",
"description": "ingredient-description",
"price": 3.6,
"unit_price": 0.36,
"unit_quantity": "100ML"
}
},
{
"quantity": "ingredients-quantity",
"ingredient": {
"image": "image-url",
"name": ingredient-name",
"description": "ingredient-description",
"price": 0.3,
"unit_price": 0.3,
"unit_quantity": "EACH"
}
},
{
"quantity": "ingredients-quantity",
"ingredient": {
"image": "image-url",
"name": ingredient-name",
"description": "ingredient-description",
"price": 2.0,
"unit_price": 0.8,
"unit_quantity": "KG"
}
}
]
}

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