[Django]-Deserialize POST request with subset of serializer fields in DRF

4👍

I prefer to use the following approach where I have 2 serializer fields for 1 model field (one read only field for details and one id/url field for creates and updates):

class CartProductSerializer(serializers.HyperlinkedModelSerializer):
    product_detail = ProductSerializer(source='product', read_only=True) 

    class Meta:
        model = CartProduct
        fields = ('url', 'cart', 'product', 'product_detail')

Note that this assumes ProductSerializer is already defined elsewhere. And I’m omitting id because we don’t really need it but you can still add it if you want.

This has the following advantages:

  • You can use the same serializer for all CRUD operations.
  • You get the nested field details on GET but can just provide ids for those nested fields on POST / PUT.
  • You don’t have to write any custom logic in your views to parse etc. – you can stick with the default generic view functionality that you get out of the box

So in your specific case, the JSON you’ll get back for a GET will be:

{
  "url": "http://localhost:8000/appUsers/1/cart/products/16/",
  "product": "http://localhost:8000/products/1/"
  "product_detail": {
    "url": "http://localhost:8000/products/1/",
    "name": "Tomatoes",
  },
  "cart": "http://localhost:8000/carts/1/"
}

And for POSTs you’ll only need to send:

{
  "product": "http://localhost:8000/products/2/"
  "cart": "http://localhost:8000/carts/1/"
}

For PUTs, include the CartProduct object’s own url in the above JSON.

👤slider

1👍

So you want the deserialized CartProductSerializer to include a nested representation of Product, while on the other hand, when serializing, you wish to provide only an id of an existing Product? You’re right that creating an additional field is one solution, and I like it best.

  1. Set product as read-only, since you do not actually accept a nested product dictionary in your serializer (you can, though).
  2. Create a new field, product_id = ModelField(model_field=Product()._meta.get_field('id')). This will allow you to pass product_id when serializing. If you want to exclude this when deserializing, you can set it as write-only. See this answer.

1👍

You can change serializer behaviour by overriding to_representation method

class CartProduct(models.Model):
    cart = models.ForeignKey('Cart', on_delete=models.CASCADE)
    product = models.ForeignKey('Product', on_delete=models.CASCADE)


class CartProductSerializer(serializers.HyperlinkedModelSerializer):
    id = serializers.ReadOnlyField()

    class Meta:
        model = CartProduct
        fields = ('id', 'url', 'product')

    def to_representation(self, instance):
        self.fields['product'] = ProductSerializer(read_only=True)
        return super().to_representation(instance)

This way your serializer will use PrimaryKeyRelatedField by default and on showing representation it will use nested ProductSerializer

NOTE: also you don’t need to explicitly supply id field if it is default AutoField, just adding it in fields meta option should be enough

Leave a comment