You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Django transaction.atomic时更新回滚但记录创建成功问题求助

问题分析与解决方案

你的问题核心在于事务交互和潜在的信号干扰,以下是具体分析和修复步骤:

1. 模型方法中get_queryset定义错误

你把get_queryset定义为Profile的实例方法,这不符合Django的设计规范——get_queryset是模型管理器的方法,而非实例方法。虽然当前代码能运行,但可能引发不可预期的查询行为,建议修正为自定义管理器:

from django.db import models, transaction

class ProfileManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset()

class Profile(models.Model):
    age = models.IntegerField()
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    objects = ProfileManager()  # 绑定自定义管理器

    @transaction.atomic
    def update_age(self, age: int) -> None:
        # 直接通过管理器查询并锁定目标行
        obj = Profile.objects.select_for_update().get(id=self.id)
        obj.age = age
        obj.save()

2. 事务范围不匹配导致的回滚

你在模型方法中单独使用@transaction.atomic,而视图中的UpdateHistory创建操作在独立事务中。这种分离可能导致:如果UpdateHistory创建触发了某些信号(如post_save),该信号若意外操作了Profile实例(比如使用了缓存的旧实例并保存),会覆盖update_age的更新,看起来像是“回滚”。

修复方案:将所有操作纳入同一事务

把视图和模型方法的事务统一,确保更新Profile和创建UpdateHistory要么同时成功,要么同时失败,避免中间状态:

视图代码修改:

from django.db import transaction

class UserProfileView(APIView):
    
    @transaction.atomic
    def put(self, request):
        profile = request.user.profile
        profile.update_age(request.data["age"])
        UpdateHistory.objects.create(user=request.user, updated_field="age")

模型方法调整(可选,移除内层事务,由视图统一管控):

class Profile(models.Model):
    # ... 其他字段和管理器定义 ...

    def update_age(self, age: int) -> None:
        obj = Profile.objects.select_for_update().get(id=self.id)
        obj.age = age
        obj.save()

3. 排查信号干扰

重点检查与UpdateHistory、User、Profile相关的信号(如post_save),确认是否有代码在创建UpdateHistory时,意外保存了缓存的旧Profile实例。例如:

# 错误示例:使用缓存的旧实例导致覆盖更新
@receiver(post_save, sender=UpdateHistory)
def on_history_created(sender, instance, **kwargs):
    # 这里的instance.user.profile是缓存的旧实例,age还是更新前的值
    instance.user.profile.save()

修复信号代码:

如果存在此类信号,需重新从数据库读取最新的Profile实例:

@receiver(post_save, sender=UpdateHistory)
def on_history_created(sender, instance, **kwargs):
    # 读取最新的Profile实例
    profile = Profile.objects.get(id=instance.user.profile.id)
    # 执行必要操作后保存
    profile.save()

4. 验证数据库引擎

确保你的数据库使用支持事务的引擎(如MySQL的InnoDB、PostgreSQL),MyISAM等不支持事务的引擎会导致select_for_update和atomic失效。


内容的提问来源于stack exchange,提问作者Temi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 10:32:33