I have an ItemCollection and Items in my Django models and I want to be able to remove Items from the collection through the UI. In a REST PUT request I add an extra boolean field deleted
for each Item to signal that an Item should be deleted.
The correct way to handle this seems to be in the update
method of the Serializer.
My problem is that this non-model deleted
field gets removed during validation, so it is not available anymore. Adding deleted
as a SerializerMethodField
did not help. For now I get my deleted
information from the initial_data
attribute of the Serializer, but that does not feel right.
My current example code is below. Does anybody know a better approach?
Models:
class ItemCollection(models.Model):
description = models.CharField(max_length=256)
class Item(models.Model):
collection = models.ForeignKey(ItemCollection, related_name="items")
Serializers:
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from models import Item, ItemCollection
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
class ItemCollectionSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True, read_only=False)
class Meta:
model = ItemCollection
def update(self, instance, validated_data):
instance.description = validated_data['description']
for item, item_obj in zip(
self.initial_data['items'], validated_data['items']):
if item['delete']:
instance.items.filter(id=item['id']).delete()
return instance
class ItemCollectionView(APIView):
def get(self, request, ic_id):
item_collection = get_object_or_404(ItemCollection, pk=ic_id)
serialized = ItemCollectionSerializer(item_collection).data
return Response(serialized)
def put(self, request, ic_id):
item_collection = get_object_or_404(ItemCollection, pk=ic_id)
serializer = ItemCollectionSerializer(
item_collection, data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data)
And an example of the json in the PUT request:
{
"id": 2,
"items": [
{
"id": 3,
"collection": 2,
"delete": true
}
],
"description": "mycoll"
}
You can add non-model fields back by overwriting the to_internal_value
fn:
def to_internal_value(self, data):
internal_value = super(MySerializer, self).to_internal_value(data)
my_non_model_field_raw_value = data.get("my_non_model_field")
my_non_model_field_value = ConvertRawValueInSomeCleverWay(my_non_model_field_raw_value)
internal_value.update({
"my_non_model_field": my_non_model_field_value
})
return internal_value
Then you can process it however you want in create
or update
.
If you're doing a PUT request, your view is probably calling self.perform_update(serializer)
. Change it for
serializer.save(<my_non_model_field>=request.data.get('<my_non_model_field>', <default_value>)
All kwargs
are passed down to validated_data to your serializer.
Make sure to properly transform incoming value (to boolean, to int, etc.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With