为了账号安全,请及时绑定邮箱和手机立即绑定

Django:如何防止数据库条目的并发修改

Django:如何防止数据库条目的并发修改

月关宝盒 2019-11-06 10:37:20
是否存在防止两个或多个用户同时修改同一数据库条目的方法?向用户显示执行第二次提交/保存操作的错误消息是可以接受的,但是数据不应被静默覆盖。我认为锁定项目不是一个选项,用户可以使用“后退”按钮或干脆关闭其浏览器,留下了锁,直到永远。
查看完整描述

3 回答

?
繁星淼淼

TA贡献1775条经验 获得超11个赞

这是我做乐观锁在Django:


updated = Entry.objects.filter(Q(id=e.id) && Q(version=e.version))\

          .update(updated_field=new_value, version=e.version+1)

if not updated:

    raise ConcurrentModificationException()

上面列出的代码可以作为Custom Manager中的方法实现。


我做出以下假设:


filter()。update()将导致单个数据库查询,因为filter是惰性的

数据库查询是原子的

这些假设足以确保之前没有其他人更新过该条目。如果以这种方式更新了多行,则应使用事务。


警告 Django Doc:


请注意,update()方法直接转换为SQL语句。这是直接更新的批量操作。它不会对你的模型运行任何save()方法,或发出pre_save或post_save信号


查看完整回答
反对 回复 2019-11-06
?
慕森王

TA贡献1777条经验 获得超3个赞

这个问题有点老了,我的回答有点晚了,但是在我理解之后,在Django 1.4中使用以下命令解决了这个问题:


select_for_update(nowait=True)

看文档


返回一个查询集,该查询集将锁定行直到事务结束,从而在支持的数据库上生成SELECT ... FOR UPDATE SQL语句。


通常,如果另一个事务已经获取了所选行之一的锁,则查询将阻塞,直到释放该锁为止。如果这不是您想要的行为,请调用select_for_update(nowait = True)。这将使呼叫成为非阻塞。如果另一个事务已经获取了冲突的锁,则在评估查询集时将引发DatabaseError。


当然,这仅在后端支持“选择更新”功能时才起作用,例如sqlite不支持。不幸的是:nowait=TrueMySql不支持:您必须在其中使用:nowait=False,它只会在释放锁之前阻塞。


查看完整回答
反对 回复 2019-11-06
?
有只小跳蛙

TA贡献1824条经验 获得超8个赞

实际上,事务在这里无济于事...除非您希望使事务运行在多个HTTP请求上(您可能不希望这样做)。


在这些情况下,我们通常使用的是“乐观锁定”。据我所知,Django ORM不支持该功能。但是,已经有一些关于添加此功能的讨论。


所以你自己一个人。基本上,您应该做的是在模型中添加一个“版本”字段,并将其作为隐藏字段传递给用户。更新的正常周期为:


读取数据并将其显示给用户

用户修改数据

用户发布数据

该应用程序将其保存回数据库中。

为了实现乐观锁定,在保存数据时,您需要检查从用户那里获得的版本是否与数据库中的版本相同,然后更新数据库并递增版本。如果不是,则表示自加载数据以来发生了更改。


您可以通过单个SQL调用来实现,例如:


UPDATE ... WHERE version = 'version_from_user';

仅当版本相同时,此调用才会更新数据库。


查看完整回答
反对 回复 2019-11-06
  • 3 回答
  • 0 关注
  • 971 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信