django rest framework: customize nested serializer - django

I have the following Django model structure:
class TypeOfIngredient(models.Model):
name = models.CharField(max_length=200,unique=True,null=False)
slug = models.SlugField(unique=True)
class Ingredient(models.Model):
name = models.CharField(max_length=200,unique=True,null=False)
slug = models.SlugField(unique=True)
typeofingredient = models.ForeignKey(TypeOfIngredient, related_name='typeof_ingredient',null=True, blank=True,on_delete=models.PROTECT)
Serializer:
class IngredientListSerializer(ModelSerializer):
class Meta:
model = Ingredient
fields = '__all__'
With the above serializer i see the following api output:
"results": [
{
"id": 1,
"name": "adrak",
"slug": "adrak",
"typeofingredient": null
},
{
"id": 2,
"name": "banana",
"slug": "banana",
"typeofingredient": 1
},
How to get "typeofingredient": "fruit" where fruit is the name field of the typeofingredient. What i am getting is the id.
I tried nested:
class IngredientListSerializer(ModelSerializer):
class Meta:
model = Ingredient
fields = '__all__'
depth = 1
Then i get the api output as:
"results": [
{
"id": 1,
"name": "adrak",
"slug": "adrak",
"typeofingredient": null
},
{
"id": 2,
"name": "banana",
"slug": "banana",
"typeofingredient": {
"id": 1,
"name": "fruit",
"slug": "fruit"
}
},
Here is showing all the details of the typeofingredient. Rather than this can i have directly "typeofingredient": "fruit"

Use serializers.ReadOnlyField
class IngredientListSerializer(ModelSerializer):
typeofingredient = serializers.ReadOnlyField(source='typeofingredient.name')
class Meta:
model = Ingredient
fields = '__all__'

You can add str method on models.py
class TypeOfIngredient(models.Model):
name = models.CharField(max_length=200,unique=True,null=False)
slug = models.SlugField(unique=True)
def __str__(self):
return str(self.name)
class Ingredient(models.Model):
name = models.CharField(max_length=200,unique=True,null=False)
slug = models.SlugField(unique=True)
typeofingredient = models.ForeignKey(TypeOfIngredient, related_name='typeof_ingredient',null=True, blank=True,on_delete=models.PROTECT)

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()

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

Django - Retrieve nested fields multiple levels deep using foreign keys

I'm struggling to write a Django GET that returns the following looking response:
{
"lists": [
{
"id": "123",
"list_order": [
{
"id": "123_1",
"order": 1,
"list_id": "123",
"item_id": 9876,
"item": {
"id": 9876,
"name": "item1",
"location": "California"
}
},
{
"id": "123_2",
"order": 2,
"list_id": "123",
"item_id": 2484,
"item": {
"id": 2484,
"name": "item2",
"location": "California"
}
}
],
"updated_date": "2018-03-15T00:00:00Z"
}
]
}
Given a list_id, the response returns the basic information on the list ("id", "updated_date"), as well as the order of items in the list. Inside each item in the list order, it also grabs the related item details (nested in "item"). I'm able to get this response without the "item" details ("id", "name", "location" fields) and with no error:
{
"lists": [
{
"id": "123",
"list_order": [
{
"id": "123_1",
"order": 1,
"list_id": "123",
"item_id": 9876
},
{
"id": "123_2",
"order": 2,
"list_id": "123",
"item_id": 2484
}
],
"updated_date": "2018-03-15T00:00:00Z"
}
]
}
Again there is no error, and I can retrieve the first nested level without any issue. The problem is getting the "item" information to show within each "list_order". Below are my models, serializers, and views.
models.py
class Lists(models.Model):
id = models.CharField(null=False, primary_key=True, max_length=900)
updated_date = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'tbl_lists'
class Items(models.Model):
id = models.BigIntegerField(primary_key=True)
name = models.TextField(blank=True, null=True)
location = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'tbl_items'
class ListOrder(models.Model):
id = models.CharField(null=False, primary_key=True, max_length=900)
list_id = models.ForeignKey(Lists, db_column='list_id', related_name='list_order')
item_id = models.ForeignKey(Items, db_column='item_id', related_name='items')
order = models.BigIntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'tbl_list_order'
serializers.py
class ItemsSerializer(serializers.ModelSerializer):
class Meta:
model = Items
fields = '__all__'
class ListOrderSerializer(serializers.ModelSerializer):
item = ItemsSerializer(many=False, read_only=True)
class Meta:
model = ListOrder
fields = '__all__'
class ListsSerializer(serializers.ModelSerializer):
list_order = ListOrderSerializer(many=True, read_only=True)
class Meta:
model = Lists
fields = '__all__'
views.py
class ListsViewSet(generics.ListCreateAPIView):
"""
API endpoint that returns a list with its meta-information
"""
queryset = Lists.objects.all()
serializer_class = ListsSerializer
def get_queryset(self):
list_id = self.kwargs['list_id']
filters = [Q(id=list_id)]
return Lists.objects.filter(*filters)
def list(self, request, list_id):
queryset = self.get_queryset()
list_serializer = ListsSerializer(queryset, many=True)
return Response({ 'lists': list_serializer.data })
I'm pretty new to Django and like what it offers so far, though maybe I'm thinking of doing this in too much of a "SQL" way. I've read about select_related() and prefetch_related(), but not sure how I would apply it to this case. Any assistance is greatly appreciated and let me know if there's any other information I can provide.
In your ListOrderSerializer you are trying to serialize item. while in ListOrder model you used the field name item_id
Solution:
In ListOrderSerializer:
class ListOrderSerializer(serializers.ModelSerializer):
item_id = ItemsSerializer(many=False, read_only=True)
...

DRF many-to-one reverse lookup

I have an API endpoint returning pets and their owners.
Each owner has a name and one or more pets
Each pet has a name and one owner
Example Django models:
class Owner(models.Model):
name = models.CharField(max_length=200)
class Pet(models.Model):
owner = models.ForeignKey(Owner, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
I've configured my API to return JSON data like this:
[
{
"id": 2,
"name": "Scotch",
"owner": {
"id": 2,
"name": "Ben"
}
},
{
"id": 3,
"name": "Fluffy",
"owner": {
"id": 1,
"name": "Fred"
}
},
{
"id": 1,
"name": "Spot",
"owner": {
"id": 1,
"name": "Fred"
}
}
]
Example DRF serializers:
class OwnerSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Owner
fields = ("id", "name")
class PetSerializer(serializers.HyperlinkedModelSerializer):
owner = OwnerSerializer()
class Meta:
model = Pet
fields = ("id", "name", "owner")
While that's all fine and dandy, I'd actually like to have an endpoint that returns a list of owners and their pets. So I'd get this data instead:
[
{
"id": 1,
"name": "Fred",
"pets": [
{ "id": 1, "name": "Spot" },
{ "id": 3, "name": "Fluffy" }
]
},
{
"id": 2,
"name": "Ben",
"pets": [
{ "id": 2, "name": "Scotch" }
]
}
]
How can I achieve that output?
You need to add pet_set field to OwnerSerializer like this:
class PetSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Pet
fields = ("id", "name")
class OwnerSerializer(serializers.HyperlinkedModelSerializer):
pet_set = PetSerializer(many=True, read_only=True)
class Meta:
model = Owner
fields = ("id", "name", "pet_set")
This will work bacause many-to-one relation default reverse lookup name is <model>_set or pet_set in your case. You can change it by using related_name:
class Pet(models.Model):
owner = models.ForeignKey(Owner, related_name='pets', on_delete=models.CASCADE)
In this case you can use pets name inside serializer:
class OwnerSerializer(serializers.HyperlinkedModelSerializer):
pets = PetSerializer(many=True, read_only=True)
Now in OwnerListView you can use this new serializer:
class OwnerListView(ListAPIView):
queryset = Owner.objects.all()
serializer_class = OwnerSerializer
Change/ add your serializer.py as following
class PetSerializer(serializers.ModelSerializer):
class Meta:
model = Pet
fields = ("id", "name")
class OwnerNewSerializer(serializers.ModelSerializer):
pets = PetSerializer(many=True, source='pet_set')
class Meta:
model = Owner
fields = ('id', 'name', 'pets')
and views.py
class OwnerAPI(viewsets.ModelViewSet):
queryset = Owner.objects.all()
serializer_class = OwnerNewSerialize

How can i Get foreign key's value instead of id , in django rest framework

Sorry for my bad English hope u can understand what i mean.
OUT PUT IM GETTING NOW:
[
{
"MainCatName": 1,
"Name": "harry potter",
"Image": "/media/101029496--sites-default-files-images-101029496-3176173-1748009911-hp.jp-1_MoxqrLp.jpg"
},
{
"MainCatName": 2,
"Name": "Princes Girl",
"Image": "/media/character_princess_rapunzel_8320d57a.jpeg"
},
{
"MainCatName": 3,
"Name": "sex in the city",
"Image": "/media/250px-SATC_Title.jpg"
},
{
"MainCatName": 4,
"Name": "who is dragon",
"Image": "/media/Reggio_calabria_museo_nazionale_mosaico_da_kaulon.jpg"
},
{
"MainCatName": 2,
"Name": "drama queen",
"Image": "/media/15241421_170761763390015_7913498865987146084_n.jpg"
}
]
WHAT I WANT :
I WANT TO TO RETURN THE FORIGN KEY'S (MainCatName) VALUE WHICH IS IN TABLE INSTEAD OF ID. ie value in the [CategoryName = models.CharField(max_length=50)] in my models.py
LIKE
[
{
"MainCatName": Story Books,
"Name": "harry potter",
"Image": "/media/101029496--sites-default-files-images-101029496-3176173-1748009911-hp.jp-1_MoxqrLp.jpg"
},
{
"MainCatName": Darama,
"Name": "Princes Girl",
"Image": "/media/character_princess_rapunzel_8320d57a.jpeg"
},
{
"MainCatName": Roamance,
"Name": "sex in the city",
"Image": "/media/250px-SATC_Title.jpg"
},
{
"MainCatName": sex,
"Name": "who is dragon",
"Image": "/media/Reggio_calabria_museo_nazionale_mosaico_da_kaulon.jpg"
},
{
"MainCatName": darama,
"Name": "drama queen",
"Image": "/media/15241421_170761763390015_7913498865987146084_n.jpg"
}
]
Here is my code :
veiws.py :
class GETCATVeiw(APIView):
def get(self, request):
data = Products.objects.only('MainCatName','Name','Image')
serializer = GETCAT(data, many=True)
return Response(serializer.data)
def post(self):
pass
models.py :
class Category(models.Model):
CategoryName = models.CharField(max_length=50)
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.CategoryName
class Products(models.Model):
MainCatName = models.ForeignKey(Category, on_delete=models.CASCADE)
Name = models.CharField(max_length=50)
Image = models.ImageField()
Price = models.IntegerField()
DiscriptionHeading = models.CharField(max_length=100)
DiscriptionParagraph = models.TextField(max_length=1000)
class Meta:
verbose_name_plural = 'Products'
def __str__(self):
return str(self.MainCatName)+' - '+self.Name+' - '+str(self.Price)+' $'
serializers.py :
class GETCAT(serializers.ModelSerializer):
class Meta:
model = Products
fields = ('MainCatName', 'Name', 'Image')
You have the SlugRelatedField for that:
class GETCAT(serializers.ModelSerializer):
MainCatName = serializers.SlugRelatedField(read_only=True, slug_field='CategoryName')
class Meta:
model = Products
fields = ('MainCatName', 'Name', 'Image')
Edit:
I'm not sure it'll work with Products.objects.only('MainCatName','Name','Image') as you'll span a database relation. Also you'll likely want to use a select_related to avoid getting N+1 DB query.
The generic Django way is to define natural keys on your model, in this case Category:
Serializing by natural key: Add a natural_key(self) method to your Category class in which you return the category's CategoryName. It has to be unique!
def natural_key(self):
return self.CategoryName
Deserializing by natural key: You want to define a default manager for your Category model:
objects = CategoryManager()
and define the get_by_natural_key(self, name) method in your CategoryManager(models.Manager) class, which returns the category:
class CategoryManager(models.Manager):
def get_by_natural_key(self, name):
return self.get(CategoryName=name)