Root out and solve the problem of error reporting of Django2 connecting MySQL perfectly

Keywords: MySQL Django Database Python

Introduction

Refer to the following article for the problem of Django2 connecting to MySQL and the solution of modifying the source code:

The interaction between Django and MySQL

However, there are many problems when the above source code modification method is used in the production environment.

This article explains in detail how to solve this problem without modifying Django source code.

Source code analysis in Django

Let's take a look at the source code of the django package for MySQL configuration in the Python interpreter we use (which can be global or virtual environment).

The source location is:

(your Python interpreter installation directory or virtual environment directory) django21 \ lib \ site packages \ Django \ DB \ backends \ MySQL \ base.py

There are a lot of contents in this base.py file. Let's pick out the most important parts to explain:

"""
MySQL database backend for Django.

Requires mysqlclient: https://pypi.org/project/mysqlclient/  # If not installed before, you need to download mysqlclient package from pypi
"""
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  # Import MySQL DB module
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
# Print this 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...

We can see that the source code limits the version of MySQL DB module!

Restrictions of Django version 1.11.20 on the version of MySQL DB

The version limit in Django version 1.11.20 is above 1.2.3:

Django 2.1 and above restrictions on the version of MySQL DB

Above 1.3.7:

Version printed when program runs successfully

If the project runs successfully, the MySQL DB version must be greater than 1.3.7

So: the key to solve this problem is to solve the version of the native MySQL DB module! django2 must be greater than 1.3.7!

Problems of MySQL DB module

In Django source code, let's print the Database (in fact, MySQL dB):

try:
    import MySQLdb as Database
    # Print this Database
    print(Database,type(Database))
except ImportError as err:
    raise ImproperlyConfigured(
        'Error loading MySQLdb module.\n'
        'Did you install mysqlclient?'
    ) from err

The result is unexpected!!!

yes! You are not wrong! The module displayed is: pymysql (print 2 times in DEBUG mode)!!!

Here is the key to solving the problem!

pymysql source code analysis***

Then, when you look back, we added two lines of pymysql related code in the init.py file of the module with the same name as the project:

import pymysql  # The import module is equivalent to executing the__init__.py file

pymysql.install_as_MySQLdb()  # implement pymysql Of install_as_MySQLdb Method

Then let's take a look at the source code of the file "init". Py "in the pymysql package installed in the current virtual environment:

"""
PyMySQL: A pure-Python MySQL client library.

### Description omitted
"""
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)

# Version of 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):

    # ellipsis

# ellipsis def Binary(x): """Return x as a binary type.""" # ellipsis

def Connect(*args, **kwargs): """ Connect to the database; see connections.Connection.__init__() for more information. """ # ellipsis

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

### Here's the key def install_as_MySQLdb(): """ After this function is called, any application that imports MySQLdb or _mysql will unwittingly actually use pymysql. """
### Importing mysql dB and mysql is equivalent to importing pymysql module!!! sys.modules["MySQLdb"] = sys.modules["_mysql"] = sys.modules["pymysql"]
__all__ = [ # Omitted ]

One of them is very critical. According to the contents of the install as MySQL DB function, you can see:

Importing MySQL DB module in this environment is equivalent to importing pymysql module.

That is to say, in Django's base.py file, version = database.version  info, actually pymysql.version  info is used. From the source code of pymysql, we can see that the value of version  info is (1, 3, 12, 'final', 0), which is the same as the result printed in our figure above!

This also explains why the pymysql module is displayed when we print the Database.

Solve with the appropriate version of pymysql

Through the above source code analysis, I believe you have come up with a solution: use the corresponding version of pymysql module for different versions of Django!

From the source code of pymysql above, we can see that the VERSION of pymysql is VERSION 0.9.3, and the corresponding VERSION of MySQL DB is set to 1.3.12, which is higher than the specified VERSION of 1.3.7, which can solve the problem of error reporting!

Using pypi to install the specified version of pymysql

Of course, in the case of networking, we can directly use pip to install the specified version of pymysql:

pip install pymysql==0.9.3 -i https://pypi.douban.com/simple

In fact, your server may not be allowed to connect to the Internet. Here is how to install pypi.

Enter pypi official website to search corresponding modules

pypi's official website is: https://pypi.org/

Then search the corresponding module:

Find the corresponding version

Find the corresponding version

Click download files

Select files of different formats

The file format is basically *. whl or *. tar.gz

Installation method of what file and tar package

The installation method of what file

[root@mycentos ~]# /opt/py3.6/ve1/bin/pip  install  xxx.whl

Installation method of tar package

Extract and enter the directory first

tar -xzvf  xxx.tar.gz
cd xxx

Execute the installation command (the directory of the virtual environment needs to be used when installing in the virtual environment)

/opt/py3.6/ve1/bin/python setup.py install

be accomplished!

Posted by dmcglone on Tue, 21 Apr 2020 00:13:28 -0700