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

Django:如何防止并发修改数据库条目,django,django-models,concurrency,transactions,atomic,Django,Django Models,Concurrency,Transactions,Atomic,是否有办法防止两个或多个用户同时修改同一数据库条目 向执行第二次提交/保存操作的用户显示错误消息是可以接受的,但数据不应被静默覆盖 我认为锁定条目不是一个选项,因为用户可能会使用“后退”按钮或简单地关闭浏览器,永远保持锁定。为了安全起见,数据库需要支持 如果字段为“自由格式”,例如文本等,并且您需要允许多个用户编辑相同的字段(您不能对数据拥有单用户所有权),则可以将原始数据存储在变量中。 当用户提交时,检查输入数据是否已从原始数据更改(如果未更改,则无需通过重写旧数据来打扰数据库), 如果原始数

是否有办法防止两个或多个用户同时修改同一数据库条目

向执行第二次提交/保存操作的用户显示错误消息是可以接受的,但数据不应被静默覆盖


我认为锁定条目不是一个选项,因为用户可能会使用“后退”按钮或简单地关闭浏览器,永远保持锁定。

为了安全起见,数据库需要支持

如果字段为“自由格式”,例如文本等,并且您需要允许多个用户编辑相同的字段(您不能对数据拥有单用户所有权),则可以将原始数据存储在变量中。 当用户提交时,检查输入数据是否已从原始数据更改(如果未更改,则无需通过重写旧数据来打扰数据库), 如果原始数据与数据库中的当前数据相比是相同的,则可以保存;如果原始数据已更改,则可以向用户显示差异,并询问用户如何操作

如果字段是数字,例如帐户余额、商店中的项目数等,则如果计算原始值(用户开始填写表单时存储的)与新值之间的差值,则可以更自动地处理该字段。您可以启动事务,读取当前值并添加差值,然后结束事务。如果不能有负值,则应在结果为负值时中止事务,并告知用户


我不认识django,所以我不能给你这些密码

为了安全起见,数据库需要支持

如果字段为“自由格式”,例如文本等,并且您需要允许多个用户编辑相同的字段(您不能对数据拥有单用户所有权),则可以将原始数据存储在变量中。 当用户提交时,检查输入数据是否已从原始数据更改(如果未更改,则无需通过重写旧数据来打扰数据库), 如果原始数据与数据库中的当前数据相比是相同的,则可以保存;如果原始数据已更改,则可以向用户显示差异,并询问用户如何操作

如果字段是数字,例如帐户余额、商店中的项目数等,则如果计算原始值(用户开始填写表单时存储的)与新值之间的差值,则可以更自动地处理该字段。您可以启动事务,读取当前值并添加差值,然后结束事务。如果不能有负值,则应在结果为负值时中止事务,并告知用户


我不认识django,所以我不能给你这些密码

事实上,交易在这里帮不了你多少忙。。。除非您希望在多个HTTP请求上运行事务(您很可能不希望这样)

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

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

  • 读取数据并将其显示给用户
  • 用户修改数据
  • 用户发布数据
  • 应用程序将其保存回数据库中
  • 要实现乐观锁定,在保存数据时,检查从用户处获得的版本是否与数据库中的版本相同,然后更新数据库并增加版本。如果不是,则意味着自加载数据后发生了更改

    您可以通过以下方式使用单个SQL调用来实现这一点:

    UPDATE ... WHERE version = 'version_from_user';
    

    只有当版本仍然相同时,此调用才会更新数据库。

    实际上,事务在这里帮不了你多少忙。。。除非您希望在多个HTTP请求上运行事务(您很可能不希望这样)

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

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

  • 读取数据并将其显示给用户
  • 用户修改数据
  • 用户发布数据
  • 应用程序将其保存回数据库中
  • 要实现乐观锁定,在保存数据时,检查从用户处获得的版本是否与数据库中的版本相同,然后更新数据库并增加版本。如果不是,则意味着自加载数据后发生了更改

    您可以通过以下方式使用单个SQL调用来实现这一点:

    UPDATE ... WHERE version = 'version_from_user';
    

    只有版本仍然相同时,此调用才会更新数据库。

    另一个需要查找的是“原子”一词。原子操作意味着您的数据库更改要么成功,要么明显失败。快速搜索显示询问Django中的原子操作。

    另一个要查找的是“原子”一词。原子操作意味着您的数据库更改要么成功,要么明显失败。快速搜索显示询问Django中的原子操作。

    您可能至少应该使用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()
    
    select_for_update(nowait=True)