create() method override in django rest - django

models.py
class Product(models.Model):
product_name = models.CharField(max_length=32)
quantity = models.IntegerField()
remarks = models.TextField(blank=True)
class Customer(models.Model):
customer_name = models.CharField(max_length=50)
address = models.CharField(max_length=100)
bill_no = models.CharField(max_length=8)
product = models.ManyToManyField(Product)
class Sell(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
date = models.DateField(auto_now_add=True)
total = models.IntegerField()
vat = models.IntegerField()
serializers.py
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
class CustomerSerializer(serializers.ModelSerializer):
product = ProductSerializer(many=True, read_only=False)
class Meta:
model = Customer
fields = '__all__'
class SellSerializer(serializers.ModelSerializer):
class Meta:
model = Sell
fields = '__all__'
And after serializers and views I get that when i browse to input sell.
How do I automatically connect Customer object to Sell so that I dont need to select the customers objects? Using token or any idea?
Also how to override create() method on customer serializer to add products details from customer view?

If you need to pass customer to the sell serializer automaticaly. You can pass it to the serializer's save method in the view:
serializer.save(customer=request.user)
You need to exclude customer field from serializer:
class SellSerializer(serializers.ModelSerializer):
class Meta:
model = Sell
exclude = ('customer',)
To save nested product you can save all new products to the list and then pass this list to the product.add() method:
class CustomerSerializer(serializers.ModelSerializer):
product = ProductSerializer(many=True, read_only=False)
class Meta:
model = Customer
fields = '__all__'
def create(self, validated_data):
product_data = validated_data.pop('product')
customer = Custome.objects.create(**validated_data)
product_lits = []
for product_details in product_data:
product_list.append(Product.objects.create(**product_details))
customer.product.add(*product_list)
return customer

Related

Postman gives 'this field already exists' while creating a product object in django rest framework

I am trying to make a post api, where a merchant can add products by selecting category, brand, collection in a merchant dashboard. But when I try to send raw json data from the postman, it says category, brand and collection already existed.
My models:
class Seller(models.Model):
seller = models.OneToOneField(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True)
business_name = models.CharField(max_length=50, blank=True)
phone_num = models.CharField(max_length=50, blank=True)
class Product(models.Model):
merchant = models.ForeignKey(Seller,on_delete=models.CASCADE,blank=True,null=True)
category = models.ManyToManyField(Category, blank=False)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
featured = models.BooleanField(default=False)
My views:
class ProductAddAPIView(CreateAPIView):
permission_classes = [IsAuthenticated]
queryset = Product.objects.all()
serializer_class = AddProductSerializer
My serializers:
class AddProductSerializer(serializers.ModelSerializer):
category = CategorySerializer(many=True,required=True)
brand = BrandSerializer(required=True)
collection = CollectionSerializer(required=True)
merchant = serializers.PrimaryKeyRelatedField(read_only=True)
variants = VariantSerializer(many=True,required=True)
class Meta:
model = Product
fields = ['id','merchant','category','brand', 'collection','featured', 'top_rated',
'name','description', 'picture','main_product_image','best_seller',
'rating','availability','warranty','services','variants']
# depth = 1
def create(self, validated_data):
user = self.context['request'].user
category_data = validated_data.pop('category',None)
brand_data = validated_data.pop('brand',None)
collection_data = validated_data.pop('collection',None)
product = Product.objects.create(merchant=user,**category_data,**brand_data,**collection_data)
return product
My urls:
path('api/addproducts', views.ProductAddAPIView.as_view(), name='api-addproducts'),
class AddProductSerializer(serializers.ModelSerializer):
category = CategorySerializer(many=True,required=True)
brand = BrandSerializer(required=True)
....
The nested serializers above (like category, brand fields) are assuming that it is creating new instance/s for category & brand. Even if you pass an id because it that field is read_only by default in ModelSerializer so it never gets included in validate_data.
If the use of the serializer is only for writing, I suppose you can use PrimaryKeyRelatedField:
class AddProductSerializer(serializers.ModelSerializer):
category = serializers.PrimaryKeyRelatedField(many=True, required=True, queryset=Category.objects.all())
brand_id = serializers.PrimaryKeyRelatedField(required=True, queryset=Brand.objects.all())
class Meta:
model = Product
fields = [
# other fields here
"brand_id", # previously "brand"
]
drf docs:
https://www.django-rest-framework.org/api-guide/relations/#primarykeyrelatedfield

Create objects in multiple nested serializer in django

I have 3 models which are related to each other via ManytoMany relation like this:
class DemandEvents(models.Model):
date = models.DateTimeField(blank=True, null=True)
quantity = models.IntegerField(default=0)
class DemandFlows(models.Model):
events = models.ManyToManyField(DemandEvents)
flow = models.ForeignKey(Flow, on_delete=models.CASCADE)
kit = models.ForeignKey(Kit, on_delete=models.CASCADE)
monthly_quantity = models.IntegerField(default=0)
class Demand(models.Model):
demand_flows = models.ManyToManyField(DemandFlows)
delivery_month = models.DateTimeField(blank=True, null=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
I am trying to create the serializers for this but keep getting confused how to handle the multi-level nesting
Serializer.py
class DemandEventsSerializer(serializers.ModelSerializer):
class Meta:
model = DemandEvents
fields = "__all__"
class DemandFlowsSerializer(serializers.ModelSerializer):
class Meta:
model = DemandFlows
fields = "__all__"
class DemandSerializer(serializers.ModelSerializer):
demand_flows = DemandFlowsSerializer(many=True)
class Meta:
model = Demand
fields = "__all__"
def create(self, validated_data):
items_objects = validated_data.pop('form_list', None)
prdcts = []
for item in items_objects:
i = DemandFlows.objects.create(**item)
prdcts.append(i)
instance = Demand.objects.create(**validated_data)
instance.demand_flows.set(prdcts)
return instance
How do I add events data to this DemandFlows?

How do I access the value of the parent class in Django?

So I have two models here
class ProductType(models.Model):
product_type = models.CharField(max_length=50)
def __str__(self):
return self.product_type
class Product(models.Model):
product_name = models.CharField(max_length=50)
product_type = models.ForeignKey(ProductType, on_delete=models.CASCADE)
product_image = models.ImageField(blank=False, null=False, upload_to="products")
product_price = models.FloatField()
product_description = models.TextField(default="Product Description")
def __str__(self):
return self.product_name
And I have created an api using django-rest-framework to be consumed by the React frontend. However, when I try to get the product_type, it'd just give me a number instead of the name as shown below. So, how can I replace that when retrieving the data with Product.objects.all() in the view?
If you want to only show product_type instead of id you can use serializers.SerializerMethodField() in your ProductSerializer and with defining source in that method you can tell serializer to return your desired field.
Example:
class ProductSerializer(serializers.ModelSerializer):
product_type = serializers.SerializerMethodField(source='product_type__product_type')
class Meta:
model = Product
fields = ('id', 'product_name', 'product_image', 'product_price', 'product_description', 'product_type')
But if you want to serialize all the ProductType itself you can define a serializer for this model and then use this serializer in your ProductSerializer to serialize the whole object of ProductType.
Example:
class ProductTypeSerializer(serializers.ModelSerializer):
class Meta:
model = ProductType
fields = ('id', 'product_type',)
class ProductSerializer(serializers.ModelSerializer):
product_type = ProductTypeSerializer()
class Meta:
model = Product
fields = ('id', 'product_name', 'product_image', 'product_price', 'product_description', 'product_type')
The second solution is also known as nested serializers.

Nested Serializer (depth/level 3)

Trying to add 3rd nested serializer using django rest framework
how to add 3rd nested realation in given code -
models.py
class Category(models.Model):
cate_id = models.AutoField(primary_key=True)
cate_name = models.CharField(max_length=45)
class Meta:
managed = False
db_table = 'category'
class SubCategory(models.Model):
sub_cate_id = models.AutoField(primary_key=True)
sub_cate_name = models.CharField(max_length=45)
sub_fk = models.ForeignKey('Category', models.CASCADE, db_column='sub_fk')
class Meta:
managed = False
db_table = 'sub_category'
class Products(models.Model):
pro_id = models.AutoField(primary_key=True)
pro_name = models.CharField(max_length=45)
description = models.CharField(max_length=45, blank=True, null=True)
price = models.IntegerField()
quantity = models.IntegerField()
pro_cate_fk = models.ForeignKey('Category', models.CASCADE, db_column='pro_cate_fk')
pro_sub_fk = models.ForeignKey('SubCategory', models.CASCADE, db_column='pro_sub_fk')
image = models.CharField(max_length=205)
class Meta:
managed = False
db_table = 'products'
from rest_framework import serializers
from .models import Category,SubCategory,Products
class ProductsSerializer(serializers.ModelSerializer):
# x= ChildTable.objects.all().values
class Meta:
model = Products
fields = ('pro_id','pro_name','description','price','quantity','image')
class SubCategorySerializer(ProductsSerializer):
products_set = ProductsSerializer(many=True, read_only=True)
class Meta:
model = SubCategory
fields = ('sub_cate_name','sub_cate_id','products_set')
class CategorySerializer(SubCategorySerializer):
subcategory_set = ProductsSerializer(many=True, read_only=True,)
# pro_subcate_set = SubCategorySerializer(many=True, read_only=True)
class Meta:
model = Category
fields = ('cate_name','cate_id','subcategory_set')
Got this error while attempting -
Got AttributeError when attempting to get a value for field `pro_name` on serializer `ProductsSerializer`. The serializer field might be named incorrectly and not match any attribute or key on the `SubCategory` instance. Original exception text was: 'SubCategory' object has no attribute 'pro_name'.
Is it possible to connect 2 already connected serializer to another serializer?
Make a test and see if it works.
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('cate_id', 'cate_name')
class SubCategorySerializer(serializers.ModelSerializer):
class Meta:
model = SubCategory
fields = ('sub_cate_id', 'sub_cate_name', 'sub_fk')
def to_representation(self, instance):
response = super().to_represntation(instance)
response['sub_fk'] = CategorySerializer(instance.sub_fk).data
return response
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Products
fields = ('pro_id', 'pro_name', 'description', 'price', 'quantity', 'pro_cate_fk', 'pro_sub_fk', 'image')
def to_representation(self, instance):
response = super().to_representation(instance)
response['pro_cate_fk'] = CategorySerializer(instance.pro_cate_fk).data
response['pro_sub_fk'] = ProductSerializer(instance.pro_sub_fk).data
return response

Django rest framework get one to many relationship data

I have a django model named Event, which references Customer model.
event_name = models.CharField(max_length=200, unique=True)
customer = models.ForeignKey(customer_models.Customer, db_index=True,
on_delete=models.SET_NULL,
related_name='customer_events', null=True)
event_location = models.CharField(max_length=200, default='')
event_date = models.DateField()
I need to get the customer list along with the latest event name for each user in the API.
Customer serializers.py file is
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = '__all__'
Customer views.py file is
class CustomerViewSet(viewsets.ModelViewSet):
queryset = Customer.objects.all()
serializer_class = CustomerSerializer
How can I accomplish this?
In your Customer model you can have a property that returns the latest event name for a Customer instance:
class Customer(models.Model):
...
#property
def latest_event_name(self):
"""Return latest event name."""
# self.customer_events.order_by('event_date').last()
latest_event = self.customer_events.order_by('-event_date').first()
return latest_event.event_name if latest_event else None
In your serializer you can then add a ReadOnlyField for latest_event_name:
class CustomerSerializer(serializers.ModelSerializer):
latest_event_name = serializers.ReadOnlyField()
class Meta:
model = Customer
fields = '__all__'