Django测试时AppConfig.ready()先于迁移执行致表不存在错误
解决Django AppConfig.ready()测试时表不存在的问题
问题根源
执行manage.py test时,Django初始化测试环境的流程中,AppConfig.ready()会在数据库迁移前触发,此时news_newstype表还未创建,直接执行数据库查询就会抛出OperationalError。
可行解决方案
方案1:延迟数据查询,按需加载
不在ready()里提前执行查询并赋值,而是改成动态获取逻辑,在生成路由正则时才触发查询:
修改AppConfig代码:
class NewsConfig(AppConfig): name = "apps.news" verbose_name = "News" @property def news_type_names(self): NewsType = self.models.get("newstype") return NewsType.objects.values_list("name", flat=True)
调整urls.py中的路由生成逻辑:
from django.apps import apps news_config = apps.get_app_config('news') news_type_names_regex = generate_regex(news_config.news_type_names) router = DefaultRouter() router.register(r'news/' + news_type_names_regex, NewsModelViewSet, basename='news')
查询操作会延迟到路由生成时执行,而测试环境在运行测试前会完成数据库迁移,此时表已存在,不会触发报错。
方案2:在ready()中检查表是否存在
通过Django数据库连接工具,判断目标表存在后再执行查询:
from django.db import connection class NewsConfig(AppConfig): name = "apps.news" verbose_name = "News" def ready(self): NewsType = self.models.get("newstype") # 检查目标表是否已创建 with connection.cursor() as cursor: table_exists = connection.introspection.table_exists(cursor, NewsType._meta.db_table) if table_exists: NewsType.names = NewsType.objects.values_list("name", flat=True) else: # 未迁移或测试环境下设置默认值,避免后续路由生成报错 NewsType.names = []
方案3:判断是否为测试环境
在项目settings.py中添加测试环境标识:
# settings.py import sys TESTING = 'test' in sys.argv
然后在AppConfig.ready()中跳过测试环境的查询操作:
from django.conf import settings class NewsConfig(AppConfig): name = "apps.news" verbose_name = "News" def ready(self): NewsType = self.models.get("newstype") if not settings.TESTING: NewsType.names = NewsType.objects.values_list("name", flat=True) else: NewsType.names = []
注意事项
- 若使用Django Channels,优先选择方案1,按需加载的逻辑不会影响ASGI应用启动,也无需依赖环境判断。
- 所有方案都要保证
news_type_names_regex生成时NewsType.names有合法值(如空列表对应默认正则),避免路由注册失败。
内容的提问来源于stack exchange,提问作者CurlyError
相关产品推荐
相关产品推荐

