Django select_for_update不能在事务之外使用

Kin*_*n2k 10 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:
Run Code Online (Sandbox Code Playgroud)

不幸的是,现在抛出一个错误:

  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.
Run Code Online (Sandbox Code Playgroud)

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

Kin*_*n2k 23

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

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:
Run Code Online (Sandbox Code Playgroud)


Dyl*_*ung 9

附录

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

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

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


小智 5

select_for_update()必须在事务中运行。

因此,使用@transaction.atomic如下所示的视图:

# "views.py"

from django.db import transaction

@transaction.atomic # Here
def some_method():    
    with transaction.atomic():
        job_qs = Job.objects.select_for_update().filter(pk=job.id)
        for job in job_qs:
Run Code Online (Sandbox Code Playgroud)

或者,with transaction.atomic():在视图中使用,如下所示:

# "views.py"

from django.db import transaction

def some_method():    
    with transaction.atomic(): # Here
        job_qs = Job.objects.select_for_update().filter(pk=job.id)
        for job in job_qs:
Run Code Online (Sandbox Code Playgroud)

或者,设置'ATOMIC_REQUESTS': True数据库设置,如下settings.py所示:

# "settings.py"

DATABASES = {
    'default':{
        'ENGINE':'django.db.backends.postgresql',
        'NAME':'postgres',
        'USER':'postgres',
        'PASSWORD':'admin',
        'HOST':'localhost',
        'PORT':'5432',
        'ATOMIC_REQUESTS': True, # Here
    },
}
Run Code Online (Sandbox Code Playgroud)