Django Rest Framework – Read nested data, write integer

Django lets you access the Item on your Pin with the item attribute, but actually stores the relationship as item_id. You can use this strategy in your serializer to get around the fact that a Python object cannot have two attributes with the same name (a problem you would encounter in your code).

The best way to do this is to use a PrimaryKeyRelatedField with a source argument. This will ensure proper validation gets done, converting "item_id": <id> to "item": <instance> during field validation (immediately before the serializer’s validate call). This allows you to manipulate the full object during validate, create, and update methods. Your final code would be:

class PinSerializer(serializers.ModelSerializer):
    item = ItemSerializer(read_only=True)
    item_id = serializers.PrimaryKeyRelatedField(write_only=True,
                                                 source="item",
                                                 queryset=Item.objects.all())

    class Meta:
        model = Pin
        fields = ('id', 'item', 'item_id',)

Note 1: I also removed source="item" on the read-field as that was redundant.

Note 2: I actually find it rather unintuitive that Django Rest is set up such that a Pin serializer without an Item serializer specified returns the item_id as "item": <id> and not "item_id": <id>, but that is beside the point.

This method can even be used with forward and reverse “Many” relationships. For example, you can use an array of pin_ids to set all the Pins on an Item with the following code:

class ItemSerializer(serializers.ModelSerializer):
    pins = PinSerializer(many=True, read_only=True)
    pin_ids = serializers.PrimaryKeyRelatedField(many=True,
                                                 write_only=True,
                                                 source="pins",
                                                 queryset=Pin.objects.all())

    class Meta:
        model = Item
        fields = ('id', 'pins', 'pin_ids',)

Another strategy that I previously recommended is to use an IntegerField to directly set the item_id. Assuming you are using a OneToOneField or ForeignKey to relate your Pin to your Item, you can set item_id to an integer without using the item field at all. This weakens the validation and can result in DB-level errors from constraints being violated. If you want to skip the validation DB call, have a specific need for the ID instead of the object in your validate/create/update code, or need simultaneously writable fields with the same source, this may be better, but I wouldn’t recommend anymore. The full line would be:

item_id = serializers.IntegerField(write_only=True)

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)