Skip to main content
 首页 » 编程设计

python之Django Rest Framework 更新嵌套的 m2m 对象。有谁知道更好的方法

2025年01月19日27langtianya

我有一个案例,用户需要更新一个实例,同时添加/编辑该实例上的 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 秒,最长的操作留在了发送电子邮件的地方。