Django使用mysql-connector-python执行migrate时出现TypeError: sequence item 1: expected a bytes-like object, str found错误
问题场景
我在Ubuntu 20.04的Python虚拟环境里,用django-admin startproject project .创建了基础项目,配置了mysql.connector.django作为数据库引擎,数据库也提前用MySQL Shell创建好了,但执行python manage.py migrate时触发了如下错误:
$ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: aqui <mysql.connector.cursor_cext._ParamSubstitutor object at 0x7f408ae1ec10> b'SELECT engine FROM information_schema.tables WHERE table_name = %s' Traceback (most recent call last): File "manage.py", line 22, in <module> main() ... File "/home/thejhoule/anon/anonproj/end/venv/lib/python3.8/site-packages/mysql/connector/cursor_cext.py", line 265, in execute stmt = RE_PY_PARAM.sub(psub, stmt) TypeError: sequence item 1: expected a bytes-like object, str found
而且首次执行迁移无表创建,第二次执行会生成两张表,尝试修改源码stmt = RE_PY_PARAM.sub(psub, stmt.decode('utf-8'))后又触发新错误:TypeError: cannot use a bytes pattern on a string-like object。
我的数据库配置如下:
DATABASES = { 'default': { 'NAME': 'dbname', 'ENGINE': 'mysql.connector.django', 'USER': 'XXXXX', 'PASSWORD': 'XXXXX', 'OPTIONS': { 'autocommit': True } } }
解决方案
这个问题本质是mysql-connector-python的Django后端和当前Django版本存在兼容性问题,SQL语句的字符串/字节类型处理不匹配导致的。下面是几个可行的解决办法,按推荐程度排序:
1. 切换到官方推荐的mysqlclient驱动(最稳妥)
mysql-connector-python的Django后端兼容性一直不如mysqlclient,Django官方也更推荐使用mysqlclient。操作步骤:
- 先卸载现有mysql-connector-python:
pip uninstall mysql-connector-python - 安装mysqlclient(Ubuntu下需要先装系统依赖,否则会编译失败):
sudo apt-get install python3-dev default-libmysqlclient-dev build-essential pip install mysqlclient - 修改
settings.py里的数据库引擎为官方MySQL后端:DATABASES = { 'default': { # ... 其他配置不变 'ENGINE': 'django.db.backends.mysql', # ... } } - 最后重新执行迁移:
python manage.py migrate
2. 调整mysql-connector-python版本
如果坚持要用mysql-connector-python,可以尝试切换到已知兼容的版本,比如8.0.27版本(这个版本修复了不少Django相关的兼容性问题):
pip uninstall mysql-connector-python pip install mysql-connector-python==8.0.27
之后重新执行python manage.py migrate即可。
3. 临时修改mysql-connector-python源码(不推荐,更新包会覆盖)
如果不想换驱动或版本,可以临时修改虚拟环境里的mysql-connector源码,解决类型不匹配问题:
- 找到虚拟环境下的
mysql/connector/cursor_cext.py文件(路径类似venv/lib/python3.8/site-packages/mysql/connector/cursor_cext.py) - 找到
RE_PY_PARAM的定义,原来的是:
修改为:RE_PY_PARAM = re.compile(b"%s")RE_PY_PARAM = re.compile(r"%s") - 或者在
execute方法里,先统一stmt的类型,把stmt = RE_PY_PARAM.sub(psub, stmt)改成:if isinstance(stmt, bytes): stmt = stmt.decode('utf-8') stmt = RE_PY_PARAM.sub(psub, stmt) - 保存后重新执行迁移。
内容的提问来源于stack exchange,提问作者João Vitor Ignácio Costa

