Django REST框架:在一个事务中保存/更新/删除相关的对象。如何?

时间:2015-11-15 11:15:19

标签: python django django-rest-framework

我想问你,如何使用DRF处理添加/编辑/删除具有许多内联对象(如Django Admin + FormSet)的对象。例如:

Publication:
  - title
  - description
  + Notofications (1 and more)
    - first_name
    - email
  + Images (1 and more)
    - title
    - url
  + Attributes (1 and more)
    - name
    - value

JSON输入:

{
     "title": "..", 
     "description": "..", 
     "notifications": [{"first_name": "", "email": ""}, ...]
     "images": [{"title": "", "url": ""}, ...]
     "attributes": [{"name": "", "value": ""}, ...]
}

所以我认为"添加"这样的结构很简单,但是如何更新" (或"修补")和"删除" (例如,其中一张图片)?整个请求应该在事务中完成,例如:如果我们对发布的标题和图像的URL进行一些编辑,并且url的格式错误,我们不应该既不保存发布对象也不保存图像对象。

REST API中是否有一个好的模式?

谢谢。

1 个答案:

答案 0 :(得分:3)

我建议使用嵌套序列化程序:

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