Reputation: 2148
I want to ask you, how to deal with adding/editing/deleting objects with many inlines objects (like in Django Admin + FormSet) using DRF. For example:
Publication:
- title
- description
+ Notofications (1 and more)
- first_name
- email
+ Images (1 and more)
- title
- url
+ Attributes (1 and more)
- name
- value
JSON input:
{
"title": "..",
"description": "..",
"notifications": [{"first_name": "", "email": ""}, ...]
"images": [{"title": "", "url": ""}, ...]
"attributes": [{"name": "", "value": ""}, ...]
}
So I think that "adding" such structure is quite simple, but how about "updating" (or "patching") and "deleting" (eg. one of the images)? The whole request should be done in transaction, eg: if we do some edit of publication's title and image's url, and the url has wrong format, we should not save neither publication object nor the image object.
Is there a good pattern in REST API?
Thank you.
Upvotes: 1
Views: 1762
Reputation: 2249
I would recommend to do it in with nested serializers:
class NestedNotificationSerializer(serializers.ModelSerializer):
class Meta:
model = models.Notification
fields = ('id', 'first_name', 'email')
def to_internal_value(self, data):
""" Return exist notification as internal value if id is provided """
if 'id' in data:
try:
return models.Notification.objects.get(id=data['id'])
except models.Notification.DoesNotExists:
raise serializers.ValidationError('Notification with id %s does not exist' % data['id'])
else:
internal_data = super(NestedNotificationSerializer, self).to_internal_value(data)
return models.Notification(**internal_data)
class PublicationSerializer(serializers.ModelSerializer):
notifications = NestedNotificationSerializer(many=True)
...
def create(self, validated_data):
notifications = validated_data.pop('notifications', [])
# create publication and its notifications in one transaction
with transaction.atomic():
publication = super(PublicationSerializer, self).create(validated_data)
for notification in notifications:
publication.notifications.add(notification)
return publication
def update(self, instance, validated_data):
notifications = validated_data.pop('notifications', [])
new_notifications = [notification for notification in notifications if notification.id is None]
existed_notifications = set([notification for notification in notifications if notification.id is not None])
# update publication and its notifications in one transaction:
with transaction.atomic():
publication = super(PublicationSerializer, self).update(instance, validated_data)
old_notifications = set(publication.notifications.all())
removed_notifications = old_notifications - existed_notifications
for notification in removed_notifications:
notification.delete()
for notification in new_notifications:
publication.notifications.add(notification)
return publication
Upvotes: 5