0%

Django 3.2加载子目录下的app报错

最近看着Django 3.2 LTS发布了,就想着啥时候把当前项目用的Django 2.2版本给升级下。结果一升级,发现项目直接无法启动了,界面上在报类似下面的错误:

django.core.exceptions.ImproperlyConfigured: Cannot import ‘myapp’. Check that ‘apps.myapp.apps.MyappConfig.name’ is correct.

我在写这个项目的时候,习惯把自己创建的app都放到了项目src下的apps目录下,一个app一个目录,要import也应该是import apps.myapp啊,这里的import myapp失败是个啥问题?难道是这个写法到Django 3.2已经不支持了?

问题在哪里?

查了下Django 3.2的release note,第一个就是”Automatic AppConfig discovery“的变更,看来加载app的部分确实是做了修改。

对比了下Django 2.2和Django 3.2的源码,django/apps/config.py的代码变更还不少,在2.2中,如果app下面没有配置default_app_config属性(我们的app很简单,都直接这么写的),则代码非常简单,直接用INSTALLED_APPS中配置的路径进行import即可。在3.2中,default_app_config属性被废除掉了,app的发现模块加了很多行,加了一堆代码去支持在app的config子类中通过配置default参数来指定默认的app_config,这个时候原来省事儿的方法就不好使了,代码里面要求app config子类中的name必须是可以import的,否则就会报错。

感觉这已经算是很breaking change了,但是看网上的说法就是:你原来正规点做就没问题,出问题了肯定是因为之前就没正规地写做过这个配置。(但是我们都是老实地用startapp创建的app啊)

正规做法

一般的做法,遵从文档的描述,应该把app目录下的apps.py中配置子类的name给写好,可直接进行import:

1
2
3
class MyappConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.myapp' # 原来直接是myapp,现在写成apps.myapp就可以成功import了

这种写法对第三方专门用来复用的app倒是无所谓,如果是给一个项目下写的特定的app那就不太友好了,需要挨个改掉这个name,而且后期如果apps目录改个名字,这里的修改又要重新进行一次。

Hacky way

从源码的逻辑来看,恢复原来AppConfig discovery的方法其实还有,就是直接在配置子类中指定default类变量是False,这样代码的逻辑会和2.2版本时候差不多。

一种改法:

1
2
3
4
class MyappConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'myapp'
default = False # 这里加了这么一个类变量

还有一种改法更简单,但是需要慎用,因为我也没测试这个变更对Django内置的一些app会有什么影响:

1
2
3
4
# 修改项目的settings.py加入以下的配置

from django.apps import AppConfig
AppConfig.default = False

观察下后续官方有啥变更吧。

如果我的文字帮到了您,那么可不可以请我喝罐可乐?