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

Use*_*ser 0 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
Run Code Online (Sandbox Code Playgroud)

JSON 输入:

{
     "title": "..", 
     "description": "..", 
     "notifications": [{"first_name": "", "email": ""}, ...]
     "images": [{"title": "", "url": ""}, ...]
     "attributes": [{"name": "", "value": ""}, ...]
}
Run Code Online (Sandbox Code Playgroud)

所以我认为“添加”这样的结构很简单,但是“更新”(或“修补”)和“删除”(例如其中一张图像)怎么样?整个请求应该在事务中完成,例如:如果我们对出版物的标题和图片的网址进行了一些编辑,并且网址格式错误,我们不应该保存出版物对象和图片对象。

REST API 中是否有好的模式?

谢谢你。

zym*_*mud 5

我建议使用嵌套序列化程序来完成它:

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
Run Code Online (Sandbox Code Playgroud)