【问题标题】:Safely deleting a Django model from the database using a transaction使用事务从数据库中安全地删除 Django 模型
【发布时间】:2011-06-13 08:58:03
【问题描述】:

在我的 Django 应用程序中,我有从数据库中删除模型的单个实例的代码。两个并发请求有可能同时尝试删除同一个模型。在这种情况下,我希望一个请求成功而另一个请求失败。我该怎么做?

问题在于,当删除带有delete() 的实例时,Django 不会返回有关命令是否成功的任何信息。这段代码说明了问题:

b0 = Book.objects.get(id=1)
b1 = Book.objects.get(id=1)
b0.delete()
b1.delete()

这两个delete() 命令中只有一个实际上删除了对象,但我不知道是哪一个。不抛出异常,也不返回任何内容来指示命令成功。在纯 SQL 中,该命令将返回删除的行数,如果值为 0,我会知道我的删除失败。

我正在使用具有默认 Read Commited 隔离级别的 PostgreSQL。我对这个级别的理解是,每个命令(SELECT、DELETE 等)都会看到数据库的快照,但下一个命令可能会看到数据库的不同快照。我相信这意味着我不能这样做:

# I believe this wont work
@commit_on_success
def view(request):
  try:
    book = Book.objects.get(id=1)
    # Possibility that the instance is deleted by the other request
    # before we get to the next delete()
    book.delete()
  except ObjectDoesntExist:
    # Already been deleted

有什么想法吗?

【问题讨论】:

    标签: database django postgresql django-models transactions


    【解决方案1】:

    您可以使用 QuerySet.delete 而不是 Model.delete 将约束直接放入 SQL DELETE 语句中:

    Book.objects.filter(pk=1).delete()
    

    这将永远不会发出 SELECT 查询,只是类似于:

    DELETE FROM Book WHERE id=1;
    

    它处理两个并发请求同时删除同一记录的竞争条件,但它不会让您知道您的删除是否首先到达那里。为此,您必须自己获取原始光标(django lets you do),.execute() 上面的 DELETE,然后选择光标的 rowcount 属性,如果您没有删除任何内容,该属性将为 0 .

    【讨论】:

      猜你喜欢
      • 2020-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-18
      • 1970-01-01
      • 1970-01-01
      • 2015-07-30
      相关资源
      最近更新 更多