【问题标题】:Django select_for_update cannot be used outside of a transactionDjango select_for_update 不能在事务之外使用
【发布时间】: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.

有什么解决方案可以解决这个问题?

【问题讨论】:

    标签: django transactions django-1.5 django-1.6


    【解决方案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
          }
      }
      

      【讨论】:

      • 这会改变所有数据库行为,而不仅仅是一段特定的代码。
      猜你喜欢
      • 2019-02-26
      • 1970-01-01
      • 2018-12-14
      • 2014-08-05
      • 1970-01-01
      • 1970-01-01
      • 2018-08-03
      • 2019-07-28
      • 1970-01-01
      相关资源
      最近更新 更多