引子
关于Django2版本连接MySQL发生的问题以及修改源码的解决方法参考下面这篇文章:
但是,上面这种修改源码的方法在生产环境中使用的话会有很多问题。
本文为大家详细讲解如何在不修改Django源码的情况下解决这个问题。
Django中的源码解析
我们来看一下我们使用的Python解释器(可以是全局的也可以是虚拟环境的)中django包有关MySQL配置的源码。
源码位置是:
(你的Python解释器安装目录或者虚拟环境目录)django21Libsite-packagesdjangodbackendsmysqlbase.py
这个base.py文件中的内容有点多,我们把最关键的部分挑出来解释一下:
"""MySQL database backend for Django.
Requires mysqlclient: https://pypi.org/project/mysqlclient/ # 之前没安装的话得从pypi中下载mysqlclient包"""
importrefrom django.core.exceptions importImproperlyConfiguredfrom django.db importutilsfrom django.db.backends importutils as backend_utilsfrom django.db.backends.base.base importBaseDatabaseWrapperfrom django.utils.functional importcached_propertytry:importMySQLdb as Database # 导入MySQLdb模块exceptImportError as err:raiseImproperlyConfigured("Error loading MySQLdb module."
"Did you install mysqlclient?")fromerrfrom 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
# 打印一下这个versionprint("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:importMySQLdb as Database#打印一下这个Database
print(Database,type(Database))exceptImportError as err:raiseImproperlyConfigured("Error loading MySQLdb module."
"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.
### 说明略去"""
importsysfrom ._compat importPY2from .constants importFIELD_TYPEfrom .converters importescape_dict, escape_sequence, escape_stringfrom .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 notNone:
VERSION_STRING= "%d.%d.%d_%s" %VERSIONelse:
VERSION_STRING= "%d.%d.%d" % VERSION[:3]
threadsafety= 1apilevel= "2.0"paramstyle= "pyformat"
classDBAPISet(frozenset):
# 省略# 省略defBinary(x):"""Return x as a binary type."""
# 省略def Connect(*args, **kwargs):"""Connect to the database; see connections.Connection.__init__() for
more information."""
# 省略from . importconnections as _orig_connif _orig_conn.Connection.__init__.__doc__ is notNone:
Connect.__doc__ = _orig_conn.Connection.__init__.__doc__
del_orig_conndef get_client_info(): #for MySQLdb compatibility
version =VERSIONif VERSION[3] isNone:
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()defthread_safe():return True #match MySQLdb.thread_safe()
### 这里是最关键的
definstall_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官网搜索对应的模块
然后搜索对应的模块:
寻找对应的版本
找到对应的版本
点击DownloadFiles
选择不同格式的文件
文件的格式基本都是 *.whl 或者 *.tar.gz
使用scp命令传递文件
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版本开发。