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信号
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,它只会在释放锁之前阻塞。
TA贡献1824条经验 获得超8个赞
实际上,事务在这里无济于事...除非您希望使事务运行在多个HTTP请求上(您可能不希望这样做)。
在这些情况下,我们通常使用的是“乐观锁定”。据我所知,Django ORM不支持该功能。但是,已经有一些关于添加此功能的讨论。
所以你自己一个人。基本上,您应该做的是在模型中添加一个“版本”字段,并将其作为隐藏字段传递给用户。更新的正常周期为:
读取数据并将其显示给用户
用户修改数据
用户发布数据
该应用程序将其保存回数据库中。
为了实现乐观锁定,在保存数据时,您需要检查从用户那里获得的版本是否与数据库中的版本相同,然后更新数据库并递增版本。如果不是,则表示自加载数据以来发生了更改。
您可以通过单个SQL调用来实现,例如:
UPDATE ... WHERE version = 'version_from_user';
仅当版本相同时,此调用才会更新数据库。
- 3 回答
- 0 关注
- 971 浏览
添加回答
举报