刨根问底,完美解决Django2版本连接MySQL报错的问题
引子
关于django2版本连接mysql发生的问题以及修改源码的解决方法参考下面这篇文章:
但是,上面这种修改源码的方法在生产环境中使用的话会有很多问题。
本文为大家详细讲解如何在不修改django源码的情况下解决这个问题。
django中的源码解析
我们来看一下我们使用的python解释器(可以是全局的也可以是虚拟环境的)中django包有关mysql配置的源码。
源码位置是:
(你的python解释器安装目录或者虚拟环境目录)\django21\lib\site-packages\django\db\backends\mysql\base.py
这个base.py文件中的内容有点多,我们把最关键的部分挑出来解释一下:
""" mysql database backend for django. requires mysqlclient: https://pypi.org/project/mysqlclient/ # 之前没安装的话得从pypi中下载mysqlclient包 """ import re from django.core.exceptions import improperlyconfigured from django.db import utils from django.db.backends import utils as backend_utils from django.db.backends.base.base import basedatabasewrapper from django.utils.functional import cached_property try: import mysqldb as database # 导入mysqldb模块 except importerror as err: raise improperlyconfigured( 'error loading mysqldb module.\n' 'did you install mysqlclient?' ) from err from mysqldb.constants import client, field_type # isort:skip from mysqldb.converters import conversions # isort:skip # some of these import mysqldb, so import them after checking if it's installed. from .client import databaseclient # isort:skip from .creation import databasecreation # isort:skip from .features import databasefeatures # isort:skip from .introspection import databaseintrospection # isort:skip from .operations import databaseoperations # isort:skip from .schema import databaseschemaeditor # isort:skip from .validation import databasevalidation # isort:skip version = database.version_info # 打印一下这个version print("version>>>>>>",version) if version < (1, 3, 7): raise improperlyconfigured('mysqlclient 1.3.7 or newer is required; you have %s.' % database.__version__)
###################
and so on...
我们可以看到,源码中对mysqldb模块的版本进行了限制!
django1.11.20版本对mysqldb的version的限制
在django1.11.20版本中的version限制是1.2.3以上:
django2.1及以上对mysqldb的version的限制
是1.3.7以上:
成功运行程序时打印的版本
项目成功运行的话,mysqldb的版本必须大于1.3.7才可以:
所以:解决这个问题的关键在于得解决本机mysqldb模块的版本!用django2的话必须大于1.3.7!
mysqldb模块的问题
还是在django源码里面,我们打印一下这个database(其实就是mysqldb):
try: import mysqldb as database # 打印一下这个database print(database,type(database)) except importerror as err: raise improperlyconfigured( 'error loading mysqldb module.\n' 'did you install mysqlclient?' ) from err
结果出乎意料!!!
是的!你没有看错!显示的模块竟然是:pymysql(打印2次是在debug模式下的设置)!!!
这里才是解决问题的关键所在!
pymysql源码解析 ***
然后大家回过头来想一下,我们配置的时候,在与项目同名的那个模块的__init__.py文件中加入了2行与pymysql相关的代码:
import pymysql # 导入模块相当于执行了这个包中的__init__.py文件 pymysql.install_as_mysqldb() # 执行pymysql的install_as_mysqldb方法
然后我们来看一下当前虚拟环境中安装的pymysql包中的__init_.py文件的源码:
""" pymysql: a pure-python mysql client library. ### 说明略去 """ import sys from ._compat import py2 from .constants import field_type from .converters import escape_dict, escape_sequence, escape_string from .err import ( warning, error, interfaceerror, dataerror, databaseerror, operationalerror, integrityerror, internalerror, notsupportederror, programmingerror, mysqlerror) from .times import ( date, time, timestamp, datefromticks, timefromticks, timestampfromticks) # pymysql的版本 version = (0, 9, 3, none) if version[3] is not none: version_string = "%d.%d.%d_%s" % version else: version_string = "%d.%d.%d" % version[:3] threadsafety = 1 apilevel = "2.0" paramstyle = "pyformat" class dbapiset(frozenset): # 省略
# 省略 def binary(x): """return x as a binary type.""" # 省略
def connect(*args, **kwargs): """ connect to the database; see connections.connection.__init__() for more information. """ # 省略
from . import connections as _orig_conn if _orig_conn.connection.__init__.__doc__ is not none: connect.__doc__ = _orig_conn.connection.__init__.__doc__ del _orig_conn def get_client_info(): # for mysqldb compatibility version = version if version[3] is none: version = version[:3] return '.'.join(map(str, version)) connect = connection = connect # we include a doctored version_info here for mysqldb compatibility version_info = (1, 3, 12, "final", 0) null = "null" __version__ = get_client_info() def thread_safe(): return true # match mysqldb.thread_safe()
### 这里是最关键的 def install_as_mysqldb(): """ after this function is called, any application that imports mysqldb or _mysql will unwittingly actually use pymysql. """
### 导入mysqldb、_mysql其实相当于导入了pymysql模块!!! sys.modules["mysqldb"] = sys.modules["_mysql"] = sys.modules["pymysql"] __all__ = [ # 略去 ]
其中有个地方十分关键,根据 install_as_mysqldb 函数中的内容可以看到:
在这个环境中导入mysqldb模块相当于导入了pymysql模块。
也就是说,在django的那个base.py文件中的 version = database.version_info,其实用的是pymysql.version_info,从pymysql的源码中我们可以看到,里面的version_info的值为(1, 3, 12, 'final', 0),跟我们上图中打印的结果是一样的!
这也就解释了了为什么我们打印database的时候显示的竟然是pymysql模块。
使用合适的pymysql的版本解决
通过上面的源码分析,相信聪明的你已经想出了解决方案了:针对不同版本的django使用对应版本的pymysql模块就可以了!
从上面pymysql的源码可以看到:pymysql的版本是 version为0.9.3,对应的mysqldb的版本设置成了1.3.12,比规定的1.3.7高,可以解决报错问题!
使用pypi安装指定版本的pymysql
当然,在联网的情况下,我们可以直接使用pip安装指定版本的pymysql:
pip install pymysql==0.9.3 -i https://pypi.douban.com/simple
实际中大家的服务器可能会禁止连接外网,下面为大家介绍一下使用pypi安装的方法。
进入pypi官网搜索对应的模块
pypi的官网为:
然后搜索对应的模块:
寻找对应的版本
找到对应的版本
点击downloadfiles
选择不同格式的文件
文件的格式基本都是 *.whl 或者 *.tar.gz
whl文件与tar包的安装方法
whl文件的安装方法
[root@mycentos ~]# /opt/py3.6/ve1/bin/pip install xxx.whl
tar包的安装方法
先解压并进入目录
tar -xzvf xxx.tar.gz cd xxx
执行安装命令 (虚拟环境中安装的话需要使用虚拟环境的目录)
/opt/py3.6/ve1/bin/python setup.py install
大功告成!
后记:关于django2.2版本的问题
我试了一下,使用django2.2版本的话,需要的mysqldb版本是 1.3.13,而0.9.3版本的pymysql不能支持,还是会报错的:
所以实际中建议大家选择合适的django版本开发。
推荐阅读
-
完美解决mysql客户端授权后连接失败的问题
-
完美解决MySQL通过localhost无法连接数据库的问题
-
MySql版本问题sql_mode=only_full_group_by的完美解决方案
-
win7系统安装2个mysql版本后连接不上数据库的问题如何解决?
-
刨根问底,完美解决Django2版本连接MySQL报错的问题
-
IDEA链接MySQL报错08001和连接成功后不显示表的问题及解决方法
-
解决navicat远程连接mysql报错10038的问题
-
解决Navicat for Mysql连接报错1251的问题(连接失败)
-
详解DBeaver连接MySQL8以上版本以及解决可能遇到的问题
-
MySql版本问题sql_mode=only_full_group_by的完美解决方案