Reputation: 25
I am writing an api's application where Order is an entity that provides information about what customers want to buy, one Order can contain different products with different quantity and different prices. The entity that includes this information in Order is called Order Detail.
And I have some problems with double nested serializers.
After post request with:
{
"external_id": "PR-123-321-123",
"details": [{
"product": {"id": 4},
"amount": 10,
"price": "12.00"
}, ... ]
}
I want to get this response:
{
"id": 1,
"status": "new",
"created_at": "2021-01-01T00:00:00",
"external_id": "PR-123-321-123",
"details": [{
"id": 1,
"product": {"id": 4, "name": "Dropbox"},
"amount": 10,
"price": "12.00"
}, ... ]
}
But now i have some error: "Cannot assign "OrderedDict()": "OrderDetail.product" must be a "Product" instance."
What's wrong?
Models:
from django.db import models
class Order(models.Model):
STATUS_CHOICES = (
('new', 'new'),
('accepted', 'accepted'),
('failed', 'failed')
)
status = models.CharField(max_length=12, choices=STATUS_CHOICES, default='new')
created_at = models.DateTimeField(auto_now_add=True)
external_id = models.CharField(max_length=128, unique=True)
class Product(models.Model):
name = models.CharField(max_length=64)
def __str__(self):
return self.name
class OrderDetail(models.Model):
order = models.ForeignKey(Order, related_name='details', on_delete=models.CASCADE)
amount = models.IntegerField()
product = models.ForeignKey(Product, on_delete=models.CASCADE)
price = models.DecimalField(max_digits=8, decimal_places=2)
def __str__(self):
return f'{self.order} detail'
Serializers:
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer
from order.models import Order, Product, OrderDetail
class ProductSerializer(ModelSerializer):
class Meta:
model = Product
fields = '__all__'
read_only_fields = ('name', )
class OrderDetailSerializer(ModelSerializer):
product = ProductSerializer()
class Meta:
model = OrderDetail
fields = (
'id',
'product',
'amount',
'price'
)
class OrderSerializer(ModelSerializer):
details = OrderDetailSerializer(many=True)
status = serializers.CharField(source='get_status_display', read_only=True)
class Meta:
model = Order
fields = (
'id',
'status',
'created_at',
'external_id',
'details'
)
def create(self, validated_data):
details_data = validated_data.pop('details')
order = Order.objects.create(**validated_data)
for detail_data in details_data:
OrderDetail.objects.create(**detail_data, order=order)
return order
Upvotes: 0
Views: 317
Reputation: 25
def create(self, *args, **kwargs):
order = Order.objects.create(external_id=self.context.get('request').data.get('external_id'))
OrderDetail.objects.bulk_create(
[OrderDetail(**order_detail, order=order, product=Product.objects.get(**order_detail.pop('product')))
for order_detail in self.context.get('request').data.get('details')]
)
return order
Upvotes: 1
Reputation: 516
class OrderSerializer(serializers.ModelSerializer):
order = OrderDetailSerializer(many=True, source='order_set', required=False)
class Meta:
model = Order
fields = (
'id',
'status',
'created_at',
'external_id',
'details'
)
def create(self, validated_data):
if 'order_set' in validated_data:
order = validated_data.pop('order_set')
else:
order = []
instance = super().create(validated_data)
for cd in order:
instance.order_set.create(**cd)
return instance
Upvotes: 0
Reputation: 1551
Modify the OrderSerializer create method, to first retrieve the Product instance before the OrderDetail object is created.
class OrderSerializer(ModelSerializer):
details = OrderDetailSerializer(many=True)
status = serializers.CharField(source='get_status_display', read_only=True)
class Meta:
model = Order
fields = (
'id',
'status',
'created_at',
'external_id',
'details'
)
def create(self, validated_data):
details_data = validated_data.pop('details')
order = Order.objects.create(**validated_data)
for detail_data in details_data:
try:
product = Product.objects.get(**detail_data.pop('product'))
OrderDetail.objects.create(**detail_data, order=order, product=product)
except (Product.DoesNotExist, Product.MultipleObjectsReturned):
raise serializers.ValidationError('Your custom error message...')
return order
Upvotes: 0