【问题标题】:Transactions not working in Django using MySQL backend使用 MySQL 后端的事务在 Django 中不起作用
【发布时间】:2018-01-08 10:08:02
【问题描述】:

我经常收到以下代码 sn-p 的完整性错误:

 class StatsManager(Manager):

    @transaction.atomic
     def create(self, **kwargs):
         kwargs.setdefault('date', date.today())
         try:
             obj = self.get_queryset().get(**kwargs)
         except self.model.DoesNotExist:
             obj = super().create(hits=1, **kwargs)  # line 28
         else:
             obj.hits = F('hits') + 1
             obj.save()
         return obj

这里是错误信息:

IntegrityError at /emploi/cours-particuliers-en-physique-chimie-en-classe-de-superieur-1-evry-14skb.html
(1062, "Duplicate entry 'EMP_VIEW-1903259-2018-01-08' for key 'statEvent'")

还有回溯:

File "/home/www/aladom_v6/www/stats/models/managers.py" in create                                                                                                                                                                                                                
  26.             obj = self.get_queryset().get(**kwargs)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/models/query.py" in get
  380.                 self.model._meta.object_name

      During handling of the above exception (Stats matching query does not exist.), another exception occurred:


File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/backends/utils.py" in execute
  65.                 return self.cursor.execute(sql, params)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django_mysql/monkey_patches.py" in execute

  35.         return orig_execute(self, sql, args)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/backends/mysql/base.py" in execute
  101.             return self.cursor.execute(query, args)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/MySQLdb/cursors.py" in execute
  250.             self.errorhandler(self, exc, value)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/MySQLdb/connections.py" in defaulterrorhandler
  50.         raise errorvalue

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/MySQLdb/cursors.py" in execute
  247.             res = self._query(query)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/MySQLdb/cursors.py" in _query
  411.         rowcount = self._do_query(q)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/MySQLdb/cursors.py" in _do_query
  374.         db.query(q)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/MySQLdb/connections.py" in query
  277.             _mysql.connection.query(self, query)


      The above exception ((1062, "Duplicate entry 'EMP_VIEW-1903259-2018-01-08' for key 'statEvent'")) was the direct cause of the following exception:



File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/core/handlers/base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/channels/handler.py" in process_exception_by_middleware 
  243.             return super(AsgiHandler, self).process_exception_by_middleware(exception, request)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/core/handlers/base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/views/generic/base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)

File "/home/www/aladom_v6/www/utils/views/behaviors.py" in dispatch
  343.             return super().dispatch(request, *args, **kwargs)

File "/home/www/aladom_v6/www/offers/views/public.py" in dispatch
  1089.         return super().dispatch(request, *args, **kwargs)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/views/generic/base.py" in dispatch
  88.         return handler(request, *args, **kwargs)

File "/home/www/aladom_v6/www/offers/views/public.py" in get
  1308.         return super().get(request, *args, **kwargs)

File "/home/www/aladom_v6/www/stats/views/behaviors.py" in get
  16.                                  target_id=self.object.pk)

File "/usr/lib/python3.4/contextlib.py" in inner
  30.                 return func(*args, **kwds)

File "/home/www/aladom_v6/www/stats/models/managers.py" in create
  28.             obj = super().create(hits=1, **kwargs)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/models/manager.py" in manager_method
  85.                 return getattr(self.get_queryset(), name)(*args, **kwargs)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/models/query.py" in create
  394.         obj.save(force_insert=True, using=self.db)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/models/base.py" in save
  808.                        force_update=force_update, update_fields=update_fields)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/models/base.py" in save_base
  838.             updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/models/base.py" in _save_table
  924.             result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/models/base.py" in _do_insert
  963.                                using=using, raw=raw)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/models/manager.py" in manager_method
  85.                 return getattr(self.get_queryset(), name)(*args, **kwargs)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/models/query.py" in _insert
  1076.         return query.get_compiler(using=using).execute_sql(return_id)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/models/sql/compiler.py" in execute_sql
  1107.                 cursor.execute(sql, params)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/backends/utils.py" in execute
  65.                 return self.cursor.execute(sql, params)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/utils.py" in __exit__
  94.                 six.reraise(dj_exc_type, dj_exc_value, traceback)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/utils/six.py" in reraise
  685.             raise value.with_traceback(tb)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/backends/utils.py" in execute
  65.                 return self.cursor.execute(sql, params)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django_mysql/monkey_patches.py" in execute
  35.         return orig_execute(self, sql, args)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/django/db/backends/mysql/base.py" in execute
  101.             return self.cursor.execute(query, args)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/MySQLdb/cursors.py" in execute
  250.             self.errorhandler(self, exc, value)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/MySQLdb/connections.py" in defaulterrorhandler
  50.         raise errorvalue

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/MySQLdb/cursors.py" in execute
  247.             res = self._query(query)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/MySQLdb/cursors.py" in _query
  411.         rowcount = self._do_query(q)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/MySQLdb/cursors.py" in _do_query
  374.         db.query(q)

File "/home/www/.virtualenvs/aladom/lib/python3.4/site-packages/MySQLdb/connections.py" in query
  277.             _mysql.connection.query(self, query)

第 28 行引发了重复输入错误,而我在上面检查了密钥是否存在,并将其封装在事务中。

我使用的是 Django 1.11,所以我尝试将 MySQL 隔离级别设置为read committed。我以为它会解决这个问题,但它仍然会发生。

我也试着反过来做:

 class StatsManager(Manager):

     def create(self, **kwargs):
         kwargs.setdefault('date', date.today())
         try:
             obj = super().create(hits=1, **kwargs)
         except IntegrityError:
             obj = self.get_queryset().get(**kwargs)  # line 27
             obj.hits = F('hits') + 1
             obj.save()
         return obj

但在这种情况下,它有时会失败并出现以下错误,因为我的请求被封装在事务中:

TransactionManagementError at /admin/moderation/serviceoffermoderation/248424/change/
An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.

这是实际预期的,与前一个错误不同。

知道发生了什么以及如何处理吗?

【问题讨论】:

    标签: mysql django transactions data-integrity


    【解决方案1】:

    看起来您可以重写代码以使用 get_or_create,它对竞争条件进行了一些处理。

    为避免 An error occurred in the current transaction 错误,您需要将可以引发 IntegrityError 的代码包装为 transaction.atomic:

     def create(self, **kwargs):
         kwargs.setdefault('date', date.today())
         try:
             with transaction.atomic():
                 obj = super().create(hits=1, **kwargs)
         except IntegrityError:
             obj = self.get_queryset().get(**kwargs)  # line 27
             obj.hits = F('hits') + 1
             obj.save()
         return obj
    

    有关更多信息,请参阅controlling transactions explicitly 上的文档。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-01-26
      • 2018-05-01
      • 2017-07-18
      • 2017-02-18
      • 2013-07-31
      • 2022-08-02
      • 2018-04-14
      相关资源
      最近更新 更多