无默认连接的Django多数据库测试问题求助
Django多数据库无默认库配置解决方案
这套无默认库的多库配置是可行的,只是遗漏了测试框架适配和路由覆盖的关键细节,具体修复步骤如下:
1. 给配置加个“占位”的default数据库
Django测试框架天生依赖default数据库的存在,哪怕你根本不用它。直接加个dummy引擎的占位配置,避免初始化时找不到库的错误:
# settings.py DATABASES = { 'default': { 'ENGINE': 'django.db.backends.dummy', # 仅占位,不实际连接 }, 'acmeapp_db': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'acmeapp.db', 'TEST': { 'NAME': ':memory:', # 用内存库跑测试更快 } }, 'auth_db': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'auth.db', 'TEST': { 'NAME': ':memory:', } } }
2. 完善数据库路由,别漏了系统应用
之前路由可能没覆盖admin、contenttypes、sessions这些Django自带应用,导致测试时这些模型会落到default库。把所有系统应用都导向auth_db:
# routers.py class MultiDBRouter: def db_for_read(self, model, **hints): if model._meta.app_label == 'acmeapp': return 'acmeapp_db' # 把所有系统核心应用都路由到auth_db elif model._meta.app_label in ['auth', 'admin', 'contenttypes', 'sessions']: return 'auth_db' # 遇到未配置的应用直接报错,避免隐式问题 raise ValueError(f"没给应用 {model._meta.app_label} 配置数据库路由") def db_for_write(self, model, **hints): return self.db_for_read(model, **hints) def allow_migrate(self, db, app_label, model_name=None, **hints): if app_label == 'acmeapp': return db == 'acmeapp_db' elif app_label in ['auth', 'admin', 'contenttypes', 'sessions']: return db == 'auth_db' return False
记得在settings里启用这个路由:
DATABASE_ROUTERS = ['yourproject.routers.MultiDBRouter']
3. 测试类要指定所有用到的数据库
之前只加acmeapp_db不够——测试过程中大概率会用到auth相关模型(比如Django测试框架默认的用户操作),必须把依赖的所有数据库都加上:
# acmeapp/tests.py from django.test import TestCase from .models import YourModel class YourModelTest(TestCase): # 同时指定两个库,确保测试时都初始化 databases = {'acmeapp_db', 'auth_db'} def test_model_creation(self): obj = YourModel.objects.create(name="测试") self.assertEqual(obj.name, "测试")
4. 测试命令正常跑就行
不用指定--database参数,Django会根据路由和测试类的配置自动创建对应测试库:
py manage.py test
单独测某个应用就加应用名:
py manage.py test acmeapp
之前报错的原因拆解
- AssertionError:测试类只指定了
acmeapp_db,但测试过程中触发了auth模型的操作(比如创建测试用户),Django找不到对应数据库的初始化配置,就会抛断言错误。 - ImproperlyConfigured循环依赖:没配置
default占位库,测试框架初始化时硬要找default,但找不到有效配置,导致循环引用的异常。 - 路由db参数始终是'default':测试初始化阶段Django会用
default处理一些系统逻辑,要么是路由没覆盖所有系统应用,要么是没加default占位库,导致请求落到了default。
内容的提问来源于stack exchange,提问作者Andez
相关产品推荐
相关产品推荐

