Nested Serializer save post foreign key in django - python

In my project, the relationship between Product and Group is ManytoOne.
When I tried to post a new product, it cannot work.
I'm sure there are more issues with this code and I will appreciate a detailed answer, because I am new to Django and Python. Thank you in advance.
models.py
class Group(models.Model):
productType = models.CharField(max_length=100)
intervalTime = models.IntegerField()
created = models.DateTimeField(auto_now_add=True)
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True,editable=False)
def __str__(self):
return self.productType
class Product(models.Model):
productName = models.CharField(max_length=255)
color = models.CharField(max_length=255)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True,editable=False)
def __str__(self):
return self.productName
serializers.py
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = ('id','productType','intervalTime')
class ProductSerializer(serializers.ModelSerializer):
group = GroupSerializer(many=False)
class Meta:
model = Product
fields = ('id','productName','color','group')
def create(self, validated_data):
group = validated_data.pop('group')
product = Product.objects.create(**validated_data)
for group_instance in group:
Group.objects.create(**group_instance, product=product)
return product
views.py
#api_view(['POST'])
def createProduct(request):
serializer = ProductSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)

First change serializers.py:
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = ('id','productType','intervalTime')
class ProductInfoSerializer(serializers.ModelSerializer):
group = GroupSerializer(read_only=True)
class Meta:
model = Product
fields = ('id','productName','color','group')
class ProductCreationSerializer(serializers.ModelSerializer):
group = serializers.SlugRelatedField(slug_field='productType', queryset=Group.objects.all())
class Meta:
model = Product
fields = ('productName','color','group')
Then change views.py:
from rest_framework import status
#api_view(['POST'])
def createProduct(request):
serializer = ProductCreationSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
instance = serializer.save()
return Response(status=status.HTTP_200_OK, data=ProductInfoSerializer(instance=instance).data)

Related

I want to use ForeignKey Serializer

I have these Models Below
class Campaign(models.Model):
title = models.CharField(max_length=120)
img = models.ImageField(upload_to='products/')
def __str__(self):
return self.title
class Product(models.Model):
title = models.CharField(max_length=100)
data = models.DateField(auto_now_add=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
image = models.ImageField(upload_to="products/")
campaign = models.ForeignKey(Campaign, on_delete=models.DO_NOTHING, null=True, blank=True, related_name="products")
offer = models.ForeignKey(Offer, on_delete=models.DO_NOTHING, null=True, blank=True)
market_price = models.PositiveIntegerField()
selling_price = models.PositiveIntegerField()
description = models.TextField()
def __str__(self):
return self.title
I want to show campaign_wise products. But i cant find any solution for this. Though i tried reading the docs of DRF
Here is my Serializer
class CampaignProductsSerializer(serializers.ModelSerializer):
products = serializers.PrimaryKeyRelatedField(read_only=True, many=True)
class Meta:
model = Campaign
fields = ['title', 'products']
Whenever i try to run this i get 'Product' object has no attribute 'products'
Here is my URL
path('campaign_products/<int:id>/', CampaignProducts.as_view()),
Here is my View:
class CampaignProducts(APIView):
def get(self, request, id):
campaigns = Campaign.objects.all()
query = Product.objects.filter(campaign_id = id)
serializer = CampaignProductsSerializer(query, many=True)
return Response(serializer.data)
In that case you should make two serializers, one for the Product and one for the Campaign, so:
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ['title'] # etc.
class CampaignProductsSerializer(serializers.ModelSerializer):
products = ProductSerializer(read_only=True, many=True)
class Meta:
model = Campaign
fields = ['title', 'products']
In the view, you then serializer a Campaign:
from django.shortcuts import get_object_or_404
# returns the title of the campaign, and the products
class CampaignProducts(APIView):
def get(self, request, id):
campaign = get_object_or_404(Campaign, id=id)
serializer = CampaignProductsSerializer(campaign)
return Response(serializer.data)
In case you do not want to include the data of the campaign, we can work with the ProductSerializer:
# returns only the related products
class CampaignProducts(APIView):
def get(self, request, id):
products = Product.objects.filter(campaign_id=id)
serializer = ProductSerializer(products, many=True)
return Response({'data': serializer.data})

How to filter a reverse related field without additional queries?

How can I filter the reverse relation queryset generated by a ModelViewSet without causing an additional n queries?
MRE:
models.py:
class Product(models.Model):
name = models.CharField(max_length=45)
image_url = models.URLField()
class Item(models.Model):
product = models.ForeignKey(Product, related_name='items', on_delete=models.CASCADE)
price = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.IntegerField()
views.py:
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.prefetch_related('items').all()
serializer_class = ProductSerializer
filter_backends = [filters.DjangoFilterBackend]
filter_class = ProductFilter
serializers.py:
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = '__all__'
class ProductSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True, read_only=True)
class Meta:
model = Product
fields = '__all__'
filters.py:
import django_filters as filters
class ProductFilter(filters.FilterSet):
price = filters.RangeFilter(field_name='items__price')
quantity = filters.RangeFilter(field_name='items__quantity')
class Meta:
model = Product
fields = {
'name': ['icontains'],
}
Filtering on the Product model's fields works fine, but the items reverse relation ignores the filtering completely.
I found a "solution" from this answer; ie. do the filtering via a SerializerMethodField:
item_fieldset = {'price', 'quantity'}
class ProductSerializer(serializers.ModelSerializer):
items = serializers.SerializerMethodField('get_items')
class Meta:
model = Product
fields = '__all__'
def get_items(self, product):
params = self.context['request'].query_params
filter_fields = item_fieldset.intersection(set(params))
filters = {field: params.get(field) for field in filter_fields}
serializer = ItemSerializer(instance=product.items.filter(**filters), many=True)
return serializer.data
But this cripples the performance of my app.
Is there a better way?

AttributeError at /apiv2/api/processorder/order/

I am trying to post multiple data into my DataBase Using Django Rest framework (DRF).
AttributeError at /apiv2/api/processorder/order/
Got AttributeError when attempting to get a value for field subcategory on serializer MyProcessOrderSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the list instance.
Original exception text was: 'list' object has no attribute 'subcategory'.
models.py
class SubCategory(models.Model):
category = models.ForeignKey(Category, related_name='subcategory', on_delete=models.CASCADE)
name = models.CharField("Food Name", max_length=50, help_text="Name of The Food")
price = models.DecimalField("Food Price", max_digits=5, decimal_places=2)
quantity = models.PositiveIntegerField("Qty.", help_text="Quantity of the food Item you want")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f'{self.name}'
class Meta:
verbose_name = 'SubCategory'
verbose_name_plural = 'SubCategories'
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
bike = models.ForeignKey(User, related_name='bike', on_delete=models.CASCADE, blank=True, null=True)
package = models.ForeignKey(PackageType, related_name='package', on_delete=models.CASCADE, blank=True, null=True)
total_price = models.DecimalField(max_digits=10, decimal_places=2, default=0000.0)
qty = models.PositiveIntegerField(default=1)
shipping_address = models.CharField("Delivery Address", max_length=150)
paid = models.BooleanField(default=False)
ordernote = models.TextField("Order Notes", null=True)
shipped = models.BooleanField(default=False)
complete = models.BooleanField(default=False)
received = models.BooleanField(default=False)
refund_requested = models.BooleanField(default=False)
refund_granted = models.BooleanField(default=False)
ref_code = models.CharField(max_length=20, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ('-created_at',)
def __str__(self):
return '{}'.format(self.id)
def order(self):
if not hasattr(self, '_order'):
self._order = self.order.all()
return self._order
'''
def get_total_cost(self):
total_cost = sum(orders.get_cost() for orders in self.order.all())
return total_cost
'''
class ProcessOrder(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
order = models.ForeignKey(Order, related_name='order', on_delete=models.CASCADE)
quantity = models.PositiveIntegerField("Qty.", default=1, help_text="Quantity of the food Item you want")
#category = models.ForeignKey(Category, related_name='category', on_delete=models.CASCADE)
subcategory = models.ForeignKey(SubCategory, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f'{self.order} -- {self.subcategory.name}'
serializers.py
class MyProcessOrderSerializer(serializers.ModelSerializer):
#subcategory_name = serializers.RelatedField(source='subcategory.id', read_only=True)
#subcategory_set = SubCategoryOrderSerializer(many=True)
class Meta:
model = ProcessOrder
fields = ('quantity', 'subcategory', 'user')
read_only_fields = ('user', )
def create(self, validated_data):
return ProcessOrder.objects.create(**validated_data)
view.pf
#api_view(['POST'])
#permission_classes([IsAuthenticated])
def processorder_view(request):
orderuser = User.objects.get(id=request.user.id)
serializer = MyProcessOrderSerializer(data=request.data, many=True)
if serializer.is_valid():
order = Order.objects.create(user=orderuser, ref_code=create_ref_code())
order.save()
processorder = serializer.save(order=order, user=orderuser)
return Response(MyProcessOrderSerializer(processorder).data, status=status.HTTP_201_CREATED)
else: #return Response("Process Order Created Successfully")
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I am now trying to make a POST such as this but getting the above error.
[{
"quantity": 16,
"subcategory": 1
},
{
"quantity": 14,
"subcategory": 3
}
]
You have to explicitly specify the relation in the serializer either with one of the built-in serializer:
Serializer relations
class MyProcessOrderSerializer(serializers.ModelSerializer):
# StringRelatedField and PrimaryKeyRelatedField are some of the built in ones.
subcategory = serializers.StringRelatedField(source='subcategory.id', read_only=True)
user = serializer.PrimaryKeyRelatedField()
class Meta:
model = ProcessOrder
fields = ('quantity', 'subcategory', 'user')
read_only_fields = ('user', )
def create(self, validated_data):
# If you want to accect new subcategory object via the endpoint you need further action here, see the docs.
return ProcessOrder.objects.create(**validated_data)
or by creating a custom subcategory serializer and then in serializer:
class UserSerializer(serializers.ModelSerializer):
class Meta:
fields = ('id', 'email', 'password', 'first_name', 'last_name')
class SubCategorySerializer(serializers.ModelSerializer):
class Meta:
fields = # whatever fields youd'e like to include
class MyProcessOrderSerializer(serializers.ModelSerializer):
# StringRelatedField is One of the built in ones.
sybcategory = SubCategorySerializer()
user = UserSerializer()
class Meta:
model = ProcessOrder
fields = ('quantity', 'subcategory', 'user')
read_only_fields = ('user', )
def create(self, validated_data):
# If you want to accect new subcategory object via the endpoint you need further action here, see the docs.
return ProcessOrder.objects.create(**validated_data)
And, I noticed you have Foreign Key to User in process order and order itself,
You could delete the user field from the process order and access it via the order relation, for that you can specify the user field in the subcategory serializer and the subcategory serializer in the process order serializer as I showed above.
And if I understand the purpose of creating a process order model, the relation should be a One to One and not Foreign key, But maybe I'm not seeing the whole picture here.
I think the problem apear in request.data in this line :
serializer = MyProcessOrderSerializer(data=request.data, many=True)
You should loop throught your object list and pass just the object not the whole list .
exemple :
{ "list_":[{"quantity": 7 ,"subcategory": 3}, {"quantity": 7 ,"subcategory": 3}] }
To get the first object from the request :
request.data.get('list_')[0]
I was able to solve the problem with this code both in my views and serialize
views.py
#api_view(['POST'])
#permission_classes([IsAuthenticated])
def processorder_view(request):
orderuser = User.objects.get(id=request.user.id)
data = request.data
order = Order.objects.create(user=orderuser, ref_code=create_ref_code())
if isinstance(data, list):
serializer = MyProcessOrderSerializer(data=request.data, many=True)
else:
serializer = MyProcessOrderSerializer(data=request.data)
if serializer.is_valid():
processorder = serializer.save(order=order, user=request.user)
return Response(status=status.HTTP_201_CREATED)
#return HttpResponse("Question created", status=201)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py
class MyProcessOrderSerializer(serializers.ModelSerializer):
#subcategory_name = serializers.RelatedField(source='subcategory.id', read_only=True)
#subcategory_set = SubCategoryOrderSerializer(many=True)
#subcategory = serializers.StringRelatedField(source='subcategory.id', read_only=True)
class Meta:
model = ProcessOrder
fields = ('quantity', 'subcategory', 'user')
read_only_fields = ('user', )
def create(self, validated_data):
return ProcessOrder.objects.create(**validated_data)
Then, I was able to send multiple data with this sample.
[
{
"subcategory": 1,
"quantity": 12
},
{
"subcategory": 3,
"quantity": 12
}
]

How can i fix "too many values to unpack" value error while file uploading in model serializer

I'm facing below issue in ModelSerializer file upload.
Issue 1) I'm trying to upload files using ModelSerializer. I have two models BlogFilesSerializer and BlogSerializer. when trying to upload a file it shows too many values to unpack error and I know its meaning why is this occurring but don't know how to handle these exceptions.
serializers.py
class BlogFilesSerializer(ModelSerializer):
created_at = serializers.SerializerMethodField()
updated_at = serializers.SerializerMethodField()
class Meta:
model = BlogFilesModel
fields = ('blog_files_id', 'blog', 'path',
'created_at', 'updated_at')
class BlogSerializer(ModelSerializer):
blog_files = serializers.SerializerMethodField()
uploaded_files = serializers.FileField(required=True, source='*')
blog_created_at = serializers.SerializerMethodField()
def get_blog_files(self, obj):
info = BlogFilesSerializer(BlogFilesModel.objects.filter(
blog=obj).order_by('-pk'), many=True)
if info.data:
for i in info.data:
user_detail = User.objects.get(pk=obj.user.id)
i.__setitem__('user_detail', UserSerializer(user_detail).data)
if i.get('user_detail'):
try:
del i['user_detail']['password']
except expression as identifier:
pass
return info.data
def get_blog_created_at(self, obj):
formatted_date = obj.created_at.strftime("%d-%m-%Y")
return formatted_date
class Meta:
model = BlogModel
fields = ('blog_id', 'user', 'title', 'content',
'status',
'blog_files', 'blog_created_at',
'uploaded_files'
)
def create(self, validated_data):
instance = BlogModel.objects.create(**validated_data)
return instance
views.py
class BlogViewSet(viewsets.ModelViewSet):
queryset = BlogModel.objects.all()
lookup_field = 'blog_id'
serializer_class = BlogSerializer
parser_classes = (MultiPartParser, FormParser,FileUploadParser)
models.py
class BlogModel(models.Model):
BLOG_STATUS = (
('PUBLISH', 'Publish'),
('DRAFT', 'Draft'),
)
blog_id = models.AutoField(primary_key=True)
user = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='blogs')
title = models.CharField(max_length=255)
content = models.TextField(blank=True, null=True)
status = models.CharField(max_length=7, choices=BLOG_STATUS)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta():
db_table = 'blogs'
verbose_name = 'Blog'
verbose_name_plural = 'Blogs'
def __str__(self):
return self.title
#property
def files(self):
return 'something'
def blog_directory_path(instance, filename):
return 'uploads/blogs/{}'.format(filename+str(datetime.now()))
class BlogFilesModel(models.Model):
blog_files_id = models.AutoField(primary_key=True)
blog = models.ForeignKey(
BlogModel, on_delete=models.CASCADE, related_name='blogs',blank=True)
path = models.FileField(blank=False, null=False,upload_to=blog_directory_path)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta():
db_table = 'blog_files'
verbose_name = 'Blog'
verbose_name_plural = 'Blogs'
def __str__(self):
return str(self.path)
Issue 2) i'm also unable to select multiple files for upload in BlogSerializer and in field uploaded_files = serializers.FileField(required=True, source='*') and their is no detailed explanation is DRF documentation. How can i add multple attribute to file field.
EDIT 1: I tried and got till here in def to_internal_value(self, data): the field.source_attrs = [] their is no key so i'm getting this error.
in my ModelSerializer i tried this uploaded_files = serializers.FileField(required=True, source='files') added files as property to BlogModel now i'm getting 'Error = files' is an invalid keyword argument for this function can someone guide me here.
Hope is have explained my problem well.
Thank you.

ManytoMany related to django Rest

modes.py
class Product(models.Model):
product_name = models.CharField(max_length=32)
quantity = models.IntegerField()
remarks = models.TextField(blank=True)
class Vendor(models.Model):
vendor_name = models.CharField(max_length=50)
address = models.CharField(max_length=100)
bill_no = models.CharField(max_length=8)
product = models.ManyToManyField(Product)
serializers.py
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
class VendorSerializer(serializers.ModelSerializer):
product = ProductSerializer(many=True, read_only=False)
class Meta:
model = Vendor
fields = '__all__'
def create(self, validate_data):
product_data = validate_data.pop('product')
vendor = Vendor.objects.create(**validate_data)
for product_data in product_data:
Product.objects.create(vendor=vendor, **product_data)
return Vendor
views.py
class VendorViewset(viewsets.ModelViewset):
serializer_class = VendorSerializer
queryset = Vendor.objects.all()
How should I write product view such that It can be demonstrated that products of certain vendor can only be viewed with url routing?
you can use the #detail_route:
from rest_framework.decorators import detail_route
class VendorViewset(viewsets.ModelViewset):
serializer_class = VendorSerializer
queryset = Vendor.objects.all()
#detail_route(methods=['GET'])
def products(request, pk=None):
qs = self.get_object().product.all()
serializer = ProductSerializer(qs, many=True)
return Response(serializer.data)
and then the vendor products will be available by
YOUCURRENT_PATH_TO_DETAIL_VENDOR/products

Categories

Resources