Django select_for_update不能在事务之外使用

时间:2014-08-22 15:58:18

标签: django transactions django-1.5 django-1.6

我使用的是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.

解决这个问题有哪些解决方案?

3 个答案:

答案 0 :(得分:22)

答案在错误中,将查询包装在事务中

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:

答案 1 :(得分:4)

附录

从Django 2.0开始,默认情况下锁定相关的行(不确定之前的行为是什么),并且可以使用select_related参数以与of相同的样式指定要锁定的行:

"默认情况下,select_for_update()会锁定查询选中的所有行。例如,除了查询集模型的行之外,select_related()中指定的相关对象的行也会被锁定。如果不需要,请使用与select_related()相同的字段语法,在select_for_update(of =(...))中指定要锁定的相关对象。使用值' self'引用查询集的模型。"

https://docs.djangoproject.com/en/dev/ref/models/querysets/#select-for-update

答案 2 :(得分:0)

对我来说,即使使用with transaction.atomic():,它也发生了。问题是我们没有在'ATOMIC_REQUESTS': True文件中设置settings.py。现在这解决了问题。

如此处所述: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
    }
}