Django + MySQL - 管理员站点 - 添加用户 - 操作错误 - SAVEPOINT不存在

时间:2017-07-10 03:21:22

标签: mysql django xampp mysql-python savepoints

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

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

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

复制步骤:

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

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

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

  4. 在MySQL中创建一个新数据库并在settings.py

  5. 中设置db config
  6. 迁移Django应用的模型: $ ./manage.py migrate

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

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

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

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

  11. 示例数据库查询日志:

    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消息。除了用于连接MySQL服务器的数据库配置/凭证外,Django的默认settings.py没有其他更改:

    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')
    

3 个答案:

答案 0 :(得分:3)

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

根本原因:SAVEPOINT问题是仅在MySQL-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驱动程序来缩小问题范围:

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

    $ pip install MySQL-python

  2. mysqlclient MySQL-python的现代版本,但有许多错误修复和改进):

    $ pip install mysqlclient

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

    $ pip install PyMySQL

    然后,添加settings.py(位于import os下方):

    try:
        import pymysql
        pymysql.install_as_MySQLdb()
    except:
        pass
    
  4. Oracle的
  5. MySQL-Connector-Python (纯Python MySQL数据库驱动程序):

    $ pip install mysql-connector-python-rf

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

    'ENGINE': 'mysql.connector.django',
    
  6. SAVEPOINT问题仅在使用 MySQL-python 连接器(#1驱动程序)时发生,而在其他连接器(#2,#3,#4驱动程序)中不存在。就我而言,我选择了 mysqlclient 。问题现在已经消失。

答案 1 :(得分: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 :(得分:0)

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

$ pip install PyMySQL

in settings.py added:

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