我有一个案例,用户需要更新一个实例,同时添加/编辑该实例上的 m2m 相关对象。
这是我的解决方案:
# models.py
class AdditionalAction(SoftDeletionModel):
ADDITIONAL_CHOICES = (
('to_bring', 'To bring'),
('to_prepare', 'To prepare'),
)
title = models.CharField(max_length=50)
type = models.CharField(choices=ADDITIONAL_CHOICES, max_length=30)
class Event(models.Model):
title= models.CharField(max_length=255)
actions = models.ManyToManyField(AdditionalAction, blank=True)
# serializers.py
class MySerializer(serializers.ModelSerializer):
def update(self, instance, validated_data):
actions_data = validated_data.pop('actions')
# Use atomic block to rollback if anything raised Exception
with transaction.atomic():
# update main object
updated_instance = super().update(instance, validated_data)
actions = []
# Loop over m2m relation data and
# create/update each action instance based on id present
for action_data in actions_data:
action_kwargs = {
'data': action_data
}
id = action_data.get('id', False)
if id:
action_kwargs['instance'] = AdditionalAction.objects.get(id=id)
actions_ser = ActionSerializerWrite(**action_kwargs)
actions_ser.is_valid(raise_exception=True)
actions.append(actions_ser.save())
updated_instance.actions.set(actions)
return updated_instance
谁能提出更好的解决方案?
附言在这种情况下可以创建或更新操作,所以我不能只在序列化器上使用 many=True,因为它还需要 instance 来更新。
请您参考如下方法:
如果您有很长的列表或在保存时触发的操作等,在此处使用带有保存的 for 循环将是一个 killer 。我会尽量避免它。 您最好使用带有 where 子句的 ORMS 更新:https://docs.djangoproject.com/en/2.0/topics/db/queries/#updating-multiple-objects-at-once甚至在写入后从数据库中读取更新的对象。
要创建新操作,您可以使用 bulk_create:https://docs.djangoproject.com/en/2.0/ref/models/querysets/#bulk-create
还有这个:https://github.com/aykut/django-bulk-update (免责声明:我不是包的贡献者或作者)。
您必须了解此方法的缺点 - 如果您使用任何后/前保存信号,这些信号将不会被更新触发。
一般来说,运行多次保存会杀死数据库,你可能会遇到难以诊断的死锁。在我从事的一个项目中,从循环中的 save() 转移到 update() 将响应时间从 30 秒左右减少到 < 10 秒,最长的操作留在了发送电子邮件的地方。
