Django select_for_update 不能在事务之外使用
Posted
技术标签:
【中文标题】Django select_for_update 不能在事务之外使用【英文标题】:Django select_for_update cannot be used outside of a transaction 【发布时间】:2014-10-16 13:09:12 【问题描述】:我使用的是 Django 1.5.1 并升级到 Django 1.6.6。
在 Django 1.5.1 中,我使用 select for update 来保证原子执行。
job_qs = Job.objects.select_for_update().filter(pk=job.id)
for job in job_qs:
不幸的是,现在这会引发错误:
File "/srv/venvs/django-picdoc/local/lib/python2.7/site-packages/django/db/models/query.py", line 96, in __iter__
self._fetch_all()
File "/srv/venvs/django-picdoc/local/lib/python2.7/site-packages/django/db/models/query.py", line 857, in _fetch_all
self._result_cache = list(self.iterator())
File "/srv/venvs/django-picdoc/local/lib/python2.7/site-packages/django/db/models/query.py", line 220, in iterator
for row in compiler.results_iter():
File "/srv/venvs/django-picdoc/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 713, in results_iter
for rows in self.execute_sql(MULTI):
File "/srv/venvs/django-picdoc/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 776, in execute_sql
sql, params = self.as_sql()
File "/srv/venvs/django-picdoc/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 147, in as_sql
raise TransactionManagementError("select_for_update cannot be used outside of a transaction.")
TransactionManagementError: select_for_update cannot be used outside of a transaction.
有什么解决方案可以解决这个问题?
【问题讨论】:
【参考方案1】:答案在错误中,将查询包装在事务中
Django 的文档位于:https://docs.djangoproject.com/en/dev/topics/db/transactions/#django.db.transaction.atomic
一种方法是:
from django.db import transaction
def some_method():
with transaction.atomic():
job_qs = Job.objects.select_for_update().filter(pk=job.id)
for job in job_qs:
【讨论】:
如果Job有外键,那么相关表是否也被锁定了? 一般选择更新数据库锁定特定的表/行,而不是相关的行。【参考方案2】:附录
从 Django 2.0 开始,默认情况下会锁定相关行(不确定之前的行为是什么),并且可以使用 of
参数以与 select_related
相同的样式指定要锁定的行:
默认情况下,
select_for_update()
锁定查询选择的所有行。例如,除了查询集模型的行之外,select_related()
中指定的相关对象的行也被锁定。如果不需要,请使用与select_related()
相同的字段语法指定要锁定在select_for_update(of=(...))
中的相关对象。使用值“self”来引用查询集的模型。
https://docs.djangoproject.com/en/dev/ref/models/querysets/#select-for-update
【讨论】:
【参考方案3】:对我来说,即使使用with transaction.atomic():
也会发生这种情况。问题是我们没有在settings.py
文件中设置'ATOMIC_REQUESTS': True
。现在这解决了问题。
如此处所述:https://docs.djangoproject.com/en/3.1/topics/db/transactions/
“在要启用此行为的每个数据库的配置中,将 ATOMIC_REQUESTS
设置为 True
。”
所以我们在settings.py
中添加了:
DATABASES =
'default':
'ENGINE': 'django.db.backends.mysql',
'NAME': os.environ['DB_NAME'],
'USER': os.environ['DB_USER'],
'PASSWORD': os.environ['DB_PASSWORD'],
'HOST': os.environ['DB_HOST'],
'PORT': '3306',
'ATOMIC_REQUESTS': True
【讨论】:
这会改变所有数据库行为,而不仅仅是一段特定的代码。以上是关于Django select_for_update 不能在事务之外使用的主要内容,如果未能解决你的问题,请参考以下文章
django select_for_update 获取关系锁
为啥不支持它的数据库可以简单地忽略 select_for_update?