运行migrate命令时出现djongo.exceptions.SQLDecodeError的解决方法
解决SimpleJWT黑名单与Djongo/MongoDB的迁移错误
问题详情
我开发的Django REST应用采用SimpleJWT做认证,按照官方文档添加rest_framework_simplejwt.token_blacklist到INSTALLED_APPS后,执行py manage.py migrate时触发djongo.exceptions.SQLDecodeError错误。使用MongoDB作为数据库,将该app从INSTALLED_APPS移除后,migrate命令可正常执行。
错误栈
Operations to perform: Apply all migrations: accounts, admin, auth, contenttypes, main, sessions, token_blacklist Running migrations: Applying token_blacklist.0008_migrate_to_bigautofield...Not implemented alter command for SQL ALTER TABLE "token_blacklist_blacklistedtoken" ALTER COLUMN "id" TYPE long Traceback (most recent call last): File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\djongo\cursor.py", line 51, in execute self.result = Query( File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\djongo\sql2mongo\query.py", line 784, in __init__ self._query = self.parse() File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\djongo\sql2mongo\query.py", line 876, in parse raise e File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\djongo\sql2mongo\query.py", line 857, in parse return handler(self, statement) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\djongo\sql2mongo\query.py", line 889, in _alter query = AlterQuery(self.db, self.connection_properties, sm, self._params) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\djongo\sql2mongo\query.py", line 425, in __init__ super().__init__(*args) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\djongo\sql2mongo\query.py", line 84, in __init__ super().__init__(*args) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\djongo\sql2mongo\query.py", line 62, in __init__ self.parse() File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\djongo\sql2mongo\query.py", line 441, in parse self._alter(statement) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\djongo\sql2mongo\query.py", line 500, in _alter raise SQLDecodeError(f'Unknown token: {tok}') djongo.exceptions.SQLDecodeError: Keyword: Unknown token: TYPE Sub SQL: None FAILED SQL: ('ALTER TABLE "token_blacklist_blacklistedtoken" ALTER COLUMN "id" TYPE long',) Params: ([],) Version: 1.3.6 The above exception was the direct cause of the following exception: Traceback (most recent call last): File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\backends\utils.py", line 85, in _execute return self.cursor.execute(sql, params) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\djongo\cursor.py", line 59, in execute raise db_exe from e djongo.database.DatabaseError The above exception was the direct cause of the following exception: Traceback (most recent call last): File "manage.py", line 22, in <module> main() File "manage.py", line 18, in main execute_from_command_line(sys.argv) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\core\management\__init__.py", line 425, in execute_from_command_line utility.execute() File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\core\management\__init__.py", line 419, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\core\management\base.py", line 373, in run_from_argv self.execute(*args, **cmd_options) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\core\management\base.py", line 417, in execute output = self.handle(*args, **options) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\core\management\base.py", line 90, in wrapped res = handle_func(*args, **kwargs) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\core\management\commands\migrate.py", line 253, in handle post_migrate_state = executor.migrate( File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\migrations\executor.py", line 126, in migrate state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\migrations\executor.py", line 156, in _migrate_all_forwards state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\migrations\executor.py", line 236, in apply_migration state = migration.apply(state, schema_editor) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\migrations\migration.py", line 125, in apply operation.database_forwards(self.app_label, schema_editor, old_state, project_state) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\migrations\operations\fields.py", line 225, in database_forwards schema_editor.alter_field(from_model, from_field, to_field) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\backends\base\schema.py", line 620, in alter_field self._alter_field(model, old_field, new_field, old_type, new_type, File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\backends\base\schema.py", line 777, in _alter_field self.execute( File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\backends\base\schema.py", line 153, in execute cursor.execute(sql, params) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\backends\utils.py", line 99, in execute return super().execute(sql, params) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\backends\utils.py", line 67, in execute return self._execute_with_wrappers(sql, params, many=False, executor=self._execute) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\backends\utils.py", line 76, in _execute_with_wrappers return executor(sql, params, many, context) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\backends\utils.py", line 85, in _execute return self.cursor.execute(sql, params) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\utils.py", line 90, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\django\db\backends\utils.py", line 85, in _execute return self.cursor.execute(sql, params) File "C:\Users\MR.Robot\.virtualenvs\fsm-GjGxZg3c\lib\site-packages\djongo\cursor.py", line 59, in execute raise db_exe from e django.db.utils.DatabaseError
INSTALLED_APPS配置
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'rest_framework_simplejwt.token_blacklist', 'drf_yasg', 'main', 'accounts', ]
依赖版本
anyio==3.5.0 asgiref==3.5.0 backports.zoneinfo==0.2.1 certifi==2021.10.8 charset-normalizer==2.0.12 coreapi==2.3.3 coreschema==0.0.4 Django==4.0.2 djangorestframework==3.13.1 djangorestframework-simplejwt==5.0.0 djongo==1.3.6 drf-yasg==1.20.0 idna==3.3 inflection==0.5.1 itypes==1.2.0 Jinja2==3.0.3 MarkupSafe==2.0.1 packaging==21.3 PyJWT==2.3.0 pymongo==3.12.1 pyparsing==3.0.7 pytz==2021.3 requests==2.27.1 ruamel.yaml==0.17.21 ruamel.yaml.clib==0.2.6 sniffio==1.2.0 sqlparse==0.2.4 tk==0.1.0 tzdata==2022.1 uritemplate==4.1.1 urllib3==1.26.8
解决方案
1. 伪造目标迁移(最快解决)
Djongo无法解析修改字段类型的SQL语句,而MongoDB本身不需要这种关系型数据库的字段类型变更操作,直接伪造执行黑名单app的0008_migrate_to_bigautofield迁移:
py manage.py migrate token_blacklist --fake 0008_migrate_to_bigautofield
之后再执行正常迁移:
py manage.py migrate
2. 降级SimpleJWT版本
SimpleJWT 5.0.0引入了这个针对关系型数据库的BigAutoField迁移,降级到4.x版本可绕过该问题:
pip install djangorestframework-simplejwt==4.8.0
3. 自定义黑名单模型(进阶)
若需要保留最新版SimpleJWT,可自定义黑名单模型继承官方抽象类,避开官方迁移:
- 在自己的app中创建模型:
from rest_framework_simplejwt.token_blacklist.models import AbstractBlacklistedToken, AbstractOutstandingToken from django.db import models class BlacklistedToken(AbstractBlacklistedToken): id = models.ObjectIdField(primary_key=True) class OutstandingToken(AbstractOutstandingToken): id = models.ObjectIdField(primary_key=True) - 在settings.py中配置:
SIMPLE_JWT = { # 其他原有配置 'BLACKLIST_AFTER_ROTATION': True, 'BLACKLIST_MODEL': 'your_app.BlacklistedToken', 'OUTSTANDING_TOKEN_MODEL': 'your_app.OutstandingToken', } - 移除INSTALLED_APPS中的
rest_framework_simplejwt.token_blacklist,执行自己app的迁移即可。
原因说明
错误源于Djongo无法解析SimpleJWT黑名单迁移中的ALTER TABLE ... ALTER COLUMN ... TYPE SQL语句——MongoDB是文档型数据库,不需要这种关系型数据库的字段类型变更操作,而SimpleJWT的该迁移是针对PostgreSQL、MySQL等关系型数据库设计的,因此与Djongo产生兼容性冲突。
内容的提问来源于stack exchange,提问作者Random User
相关产品推荐
相关产品推荐

