0%

Fix Django 3.2 App Import Errors for Apps in a Subdirectory

When Django 3.2 LTS came out, I started thinking about upgrading a project from Django 2.2. As soon as I tried, the project would not even start. It failed with an error like this:

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

I usually organize my own apps under an apps directory inside the project source tree, so imports look like apps.myapp. Seeing Django try to import just myapp was surprising.

Where is the problem?

Looking at the Django 3.2 release notes, the first relevant change is “Automatic AppConfig discovery”. That is where the behavior changed.

Comparing the source between Django 2.2 and 3.2, the logic in django/apps/config.py changed quite a bit. In Django 2.2, if an app did not define default_app_config, Django simply imported the app path listed in INSTALLED_APPS.

In Django 3.2, default_app_config was deprecated, and app discovery gained new logic to support selecting a default AppConfig class through a default attribute. That means the name attribute in the AppConfig class must now be a valid import path, otherwise Django raises an error.

This feels like a fairly breaking change in practice, although the usual answer online is that if you had configured AppConfig more explicitly in the first place, you would not hit the problem.

The proper fix

The documented approach is to make sure the name attribute in apps.py uses the real import path:

1
2
3
class MyappConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.myapp'

This is fine for reusable third-party apps, but for internal project apps it is a bit annoying. You have to update every app, and if the parent package name changes later, you have to update them again.

A hacky alternative

Reading through the source code suggests that it is still possible to get behavior closer to Django 2.2 by explicitly setting default = False in the AppConfig subclass:

1
2
3
4
class MyappConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'myapp'
default = False

There is also an even simpler approach, though it should be used carefully because I did not test its impact on Django’s built-in apps:

1
2
from django.apps import AppConfig
AppConfig.default = False

For now, I am watching to see whether Django changes this behavior again in later releases.

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