使用事务从数据库安全地删除Django模型

时间:2011-01-21 19:29:21

标签: database django postgresql django-models transactions

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

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

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

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

我正在使用PostgreSQL默认的Read Commited隔离级别。我对这个级别的理解是每个命令(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

有什么想法吗?

1 个答案:

答案 0 :(得分:4)

您可以使用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结束删除任何东西。