motam
motam

Reputation: 747

Proper way to have a nested writable serialization django drf

Assum that I have two models with two serializers, one has another as a nested serializer, as shown below:

class Item(models.Model):
  ...
  discounts = ManyToManyField(Discount)
  gift_discounts = ManyToManyField(GiftDiscount)
  ...

class Billing(models.Model):
  ...
  items = ManyToManyField(Item)
  ...

# serializers
class ItemSerializer(serializers.ModelSerializer):
  ...
  def create(self, validated_data):
    discounts = validated_data.pop('discounts')
    gift_discounts = validated_data.pop('gift_discounts')
    item = super(ItemSerializer, self).create(**validated_data)
    for discount in discounts:
      item.discounts.add(discount)
    for gift_discount in gift_discounts:
      item.gift_discounts.add(gift_discount)

class BillingSerializer(serializers.ModelSerializer):
  items = ItemSerializer(queryset=Item.objects.all(), many=True)
  ...
  def create(self, validated_data):
    items = validated_data.pop('items')
    billing = super(BillingSerializer, self).create(**validated_data)

    for item in items:
      discounts = item.pop('discounts')
      gift_discounts = item.pop('gift_discounts')

      sell_item = Item.objects.create(**item)

      for discount in discounts:
        sell_item.discounts.add(discount)

      for gift_discount in gift_discounts:
        sell_item.gift_discounts.add(gift_discount)

As you can see for such a scenario I had to write twice a same code for creation of item one in item serializer and another in billing serializer, this is against the DRY rule and it could getting more complicated and bug prone as code advances. I am looking a way to write this code just once and use it in both places.

Maybe having a class method in ItemSerializer is a solution but not complete solution there you have not many ItemSerializer methods and members which you may need.I think the best possible solution is creating a serializer, not with the raw data but with the validated one, because in Billing create method we have the validated data of item.

I am using django 1.11 and DRF 3.8.2;

Upvotes: 0

Views: 236

Answers (1)

Ehsan Nouri
Ehsan Nouri

Reputation: 2040

you can use the Item serializer to create the items in BillingSerializer too, like this:

class BillingSerializer(serializers.ModelSerializer):
  items = ItemSerializer(queryset=Item.objects.all(), many=True)
  ...
  def create(self, validated_data):
    items_validated_data = validated_data.pop('items')
    instance = super(BillingSerializer, self).create(validated_data)
    items = ItemSerializer(many=True).create(items_validated_data)

    instance.items.set(items)
    return billing

Upvotes: 1

Related Questions