Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement a writable serializer for multilevel nested relationships in django rest framework

In drf3 you can now implement a writable nested serializer by overriding the create() method and handling validated_data yourself. However, what if you've a multi-level nested relationship in the models like so:

class Order(models.Model):
    """
    Order model to aggregate all the shipments created by a user at a particular time.
    """
    created_at = models.DateTimeField(
        verbose_name='created at',
        auto_now_add=True
    )
    updated_at = models.DateTimeField(
        verbose_name='updated at',
        auto_now=True
    )

class Shipment(models.Model):
    """
    Many to One Relationship with the Orders Model. Aggregates all the details of a shipment being sent.
    """
    created_at = models.DateTimeField(
        verbose_name='created at',
        auto_now_add=True
    )
    updated_at = models.DateTimeField(
        verbose_name='updated at',
        auto_now=True
    )
    order = models.ForeignKey(
        to=Order
    )

class ItemDetail(models.Model):
    """
    Specifies details of the shipment contents. One to One relationship with the Shipment Model.
    """
    shipment = models.OneToOneField(
        to=Shipment,
        primary_key=True
    )
    CONTENT_TYPES = (
        ('D', 'Documents'),
        ('P', 'Products')
    )
    content = models.CharField(
        verbose_name='package contents',
        max_length=1,
        choices=CONTENT_TYPES,
        default='P'
    )

How would I write a serializer for order with custom create method to handle such a case? All the examples I've seen including the one on the official page has just one level of nested relationship.

Read is working fine with the depth argument. However, I'd really appreciate any help with writing the create/update method.

like image 725
Vatsal Shah Avatar asked Sep 27 '22 05:09

Vatsal Shah


2 Answers

The writable nested serializer is explained in the documentation.

Please do not recreate a new serializer in the create/update. Once you hit the top most serializer create/update, all your data are validated, including the nested ones.

At this point, you'll have to write by yourself the mapping from the validated_data to your various objects.

I tried to make it automatic for DRF 2.x but it turns out that there was too many use case, some of which were exclusives. Therefore DRF leaves that work upon the developer - i.e. you.

like image 190
Linovia Avatar answered Oct 03 '22 15:10

Linovia


One way is to have two serializers, firs ItemDetailSerializer which will call ShipmentSirializer to handle Order and Shipment creation and then create ItemDetail itself.

Something like:

class ItemDetailSerializer(serializers.ModelSerializer):
    """
    # Describe here all relationships
    """
    class Meta:
        model = ItemDetail

    def create(self, validated_data):

        shipment = validated_data.pop('shipment')
        shipment_serializer = ShipmentSerializer(data=shipment)
        if shipment_serializer.is_valid()
            shipment_serializer.save()

            item_detail = ItemDetail.objects.create(shipment=shipment_serializer.instance,
                                                    **validated_data)
            return item_detail

And for the ShipmentSerializer you can follow documentation about nested relations, that you've mentioned.

like image 41
DevilPinky Avatar answered Oct 03 '22 15:10

DevilPinky