Django select_for_update不能在事务之外使用

lskq00tm  于 2023-02-10  发布在  Go
关注(0)|答案(4)|浏览(158)

我用的是Django 1.5.1,升级到了Django 1.6.6。
在Django1.5.1中,我使用select来更新,以保证原子执行。

# "views.py"

from django.db import transaction

def some_method():    
    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.

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

relj7zay

relj7zay1#

答案在错误中,请将查询 Package 在事务中
Django的文档位于:https://docs.djangoproject.com/en/dev/topics/db/transactions/#django.db.transaction.atomic
一种方法是:

# "views.py"

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:
oyxsuwqo

oyxsuwqo2#

附录

从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

ocebsuys

ocebsuys3#

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:

或者,在视图中使用**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:

或者,将**'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
    },
}
watbbzwu

watbbzwu4#

对我来说,即使在使用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
    }
}

相关问题