Django + MySQL - 管理站点 - 添加用户 - OperationalError - SAVEPOINT 不存在

Posted

技术标签:

【中文标题】Django + MySQL - 管理站点 - 添加用户 - OperationalError - SAVEPOINT 不存在【英文标题】:Django + MySQL - Admin Site - Add User - OperationalError - SAVEPOINT does not exist 【发布时间】:2017-12-13 16:10:47 【问题描述】:

我们正在尝试自定义用户模型和行为,但后来我们注意到,即使是 默认的 Django 安装,在通过 Django Admin 添加新用户时也会出现问题:

即使在其他 Django 版本中也会出现此问题(在 Django 1.8 和最新版本 Django 1.11.3 中尝试过)。令人惊讶的是,使用 SQLite 或 PostgreSQL 数据库时不会发生此问题。此外,通过$./manage.py createuser 以编程方式添加用户也可以。也可以通过终端编辑现有用途,例如先前创建的 admin 超级用户。 Group 的 CRUD 机制按预期工作,因此只有 Add User 视图受到影响。

可能的故障点包括 Django 核心(任何版本)、MySQL 二进制文件(捆绑在 XAMPP for Mac 中,也尝试过各种版本)或 MySQL-Python 连接器(版本 1.2.5)。类似问题here,使用 Django 1.10mysql

复制步骤:

    安装最新的 Django 版本: $ pip install django

    安装 Python-MySQL 驱动程序: $ pip install MySQL-python

    创建一个新项目: $ django-admin.py startproject sandbox

    在 MySQL 中新建一个数据库,并在 settings.py 中设置 db config

    迁移 Django 应用的模型: $ ./manage.py migrate

    创建一个admin 超级用户: $ ./manage.py createsuperuser

    运行 Django 的捆绑服务器: $ ./manage.py runserver

    转到http://127.0.0.1:8000/admin/login,并使用admin 超级用户凭据登录。

    尝试点击用户添加按钮。屏幕截图中的附加错误将被触发。

示例数据库查询日志:

Query   SET NAMES utf8
Query   set autocommit=1
Query   SELECT `django_session`.`session_key`, `django_session`.`session_data`, `django_session`.`expire_date` FROM `django_session` WHERE (`django_session`.`session_key` = 'ikql6mk9voxq4g0go9avuvuxxrpvwx9w' AND `django_session`.`expire_date` > '2017-07-10 06:58:15.823513')
Query   SELECT `auth_user`.`id`, `auth_user`.`password`, `auth_user`.`last_login`, `auth_user`.`is_superuser`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`last_name`, `auth_user`.`email`, `auth_user`.`is_staff`, `auth_user`.`is_active`, `auth_user`.`date_joined` FROM `auth_user` WHERE `auth_user`.`id` = 1
Query   SAVEPOINT `s123145414516736_x1`
Query   RELEASE SAVEPOINT `s123145414516736_x1`
Query   ROLLBACK TO SAVEPOINT `s123145414516736_x1`
Query   rollback
Query   set autocommit=1
Quit

SAVEPOINT ROLLBACK 似乎在 SAVEPOINT RELEASE 之后完成,导致 SAVEPOINT 丢失。基于MySQL's SavePoint docs,自然顺序似乎是 ROLLBACK 然后 RELEASE。

这是 Traceback 消息。 Django 的默认 settings.py 没有其他变化,除了用于连接 MySQL 服务器的数据库配置/凭据:

Environment:


Request Method: GET
Request URL: http://127.0.0.1:8000/admin/auth/user/add/

Django Version: 1.11.3
Python Version: 2.7.8
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/contrib/admin/options.py" in wrapper
  551.                 return self.admin_site.admin_view(view)(*args, **kwargs)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  149.                     response = view_func(request, *args, **kwargs)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  57.         response = view_func(request, *args, **kwargs)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/contrib/admin/sites.py" in inner
  224.             return view(request, *args, **kwargs)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapper
  67.             return bound_func(*args, **kwargs)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/views/decorators/debug.py" in sensitive_post_parameters_wrapper
  76.             return view(request, *args, **kwargs)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/utils/decorators.py" in bound_func
  63.                 return func.__get__(self, type(self))(*args2, **kwargs2)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapper
  67.             return bound_func(*args, **kwargs)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  149.                     response = view_func(request, *args, **kwargs)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/utils/decorators.py" in bound_func
  63.                 return func.__get__(self, type(self))(*args2, **kwargs2)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/contrib/auth/admin.py" in add_view
  103.             return self._add_view(request, form_url, extra_context)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/contrib/auth/admin.py" in _add_view
  131.                                                extra_context)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/contrib/admin/options.py" in add_view
  1508.         return self.changeform_view(request, None, form_url, extra_context)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapper
  67.             return bound_func(*args, **kwargs)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  149.                     response = view_func(request, *args, **kwargs)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/utils/decorators.py" in bound_func
  63.                 return func.__get__(self, type(self))(*args2, **kwargs2)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/contrib/admin/options.py" in changeform_view
  1408.             return self._changeform_view(request, object_id, form_url, extra_context)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/db/transaction.py" in __exit__
  210.                                 connection.savepoint_rollback(sid)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/db/backends/base/base.py" in savepoint_rollback
  348.         self._savepoint_rollback(sid)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/db/backends/base/base.py" in _savepoint_rollback
  308.             cursor.execute(self.ops.savepoint_rollback_sql(sid))

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/db/backends/utils.py" in execute
  80.             return super(CursorDebugWrapper, self).execute(sql, params)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/db/backends/utils.py" in execute
  65.                 return self.cursor.execute(sql, params)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/db/utils.py" in __exit__
  94.                 six.reraise(dj_exc_type, dj_exc_value, traceback)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/db/backends/utils.py" in execute
  63.                 return self.cursor.execute(sql)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/django/db/backends/mysql/base.py" in execute
  101.             return self.cursor.execute(query, args)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/MySQLdb/cursors.py" in execute
  205.             self.errorhandler(self, exc, value)

File "/Users/ranelpadon/.virtualenvs/django__1_11/lib/python2.7/site-packages/MySQLdb/connections.py" in defaulterrorhandler
  36.     raise errorclass, errorvalue

Exception Type: OperationalError at /admin/auth/user/add/
Exception Value: (1305, 'SAVEPOINT s123145452511232_x1 does not exist')

【问题讨论】:

我认为有一些 IntegrityError。启用日志记录并检查。 感谢您的建议。我尝试启用 MySQL 日志并获得了更多信息: 我还编辑了帖子以包含 MySQL 日志并强调 SAVEPOINT 工作流/顺序不一致。可能是 Django 核心中的错误。 我发现这可能发生在集群的 MySQL/Maria Db 实例上 - 也许从属服务器之间的复制速度很慢? 【参考方案1】:

我通过覆盖UserAdmin 解决了这个问题。集群数据库中的嵌套原子事务似乎存在问题。

将以下行添加到继承自基础 Django UserAdmin 的自定义 UserAdmin:

@sensitive_post_parameters_m
@csrf_protect_m
def add_view(self, request, form_url='', extra_context=None):
    return self._add_view(request, form_url, extra_context)

【讨论】:

【参考方案2】:

我有一个项目刚刚从 python 2 升级到 3,发生这种情况是因为我安装了 mysqlclient,这就是从上述建议之一中获取的原因(在 python 3.5 上):

$ pip install PyMySQL

in settings.py added:

try:
    import pymysql
    pymysql.install_as_MySQLdb()
except:
    pass

【讨论】:

【参考方案3】:

这个错误困扰了我很长时间,所以我决定进一步挖掘并尝试一劳永逸地解决它。

根本原因:SAVEPOINT 问题是一个仅在 MySQL-Python 连接器中出现的错误。

修复: 使用其他适用于 Python 的 MySQL 驱动程序(例如 mysqlclient)。

详情/调查结果:

在 Homebrew、MAMP 和 XAMPP for Mac 中尝试了 MySQL 二进制文件。 尝试了各种 MySQL 版本,5.6 (libmysqlclient.18.dylib) 和 5.7 (libmysqlclient.20.dylib)。 尝试了各种 Python 的 MySQL 驱动程序。

通过更改 MySQL 二进制文件/版本未找到任何关系。但我通过测试 Python 中常用的各种 MySQL 驱动程序缩小了问题范围:

    MySQLdb(广泛使用但旧的数据库连接器,最后一次提交是 7 年前!):

    $ pip install MySQL-python

    mysqlclientMySQL-python 的现代版本,但有很多错误修复和改进):

    $ pip install mysqlclient

    PyMySQL(纯 Python MySQL 数据库驱动程序):

    $ pip install PyMySQL

    然后,添加settings.py(就在import os下方):

    try:
        import pymysql
        pymysql.install_as_MySQLdb()
    except:
        pass
    

    MySQL-Connector-Python by Oracle(纯 Python MySQL 数据库驱动程序):

    $ pip install mysql-connector-python-rf

    然后,在settings.py中编辑数据库的ENGINE配置:

    'ENGINE': 'mysql.connector.django',
    

SAVEPOINT 问题仅在使用 MySQL-python 连接器(#1 驱动程序)时出现,而在其他连接器(#2、#3、#4 驱动程序)中不会出现。就我而言,我选择了 mysqlclient。现在问题已经解决了。

【讨论】:

我看到与 mysqlclient 1.4.2.post1 的错误完全一样 mysqlclient 1.4.6同样的问题 mysqlclient==2.0.1也有这个问题

以上是关于Django + MySQL - 管理站点 - 添加用户 - OperationalError - SAVEPOINT 不存在的主要内容,如果未能解决你的问题,请参考以下文章

python django 站点管理 配置mysql数据库

Django学习之启用管理站点详解

django配置超级管理员

Python脚本之django---mysql-记录主机性能数据到数据库-web站点管理数据库及web显示命令执行结果

Django站点管理——管理站点

从 PHP 站点执行 python 脚本——我需要 Django 吗?可以在树莓派上完成吗? [关闭]