Channels: Channels 3 asgi.py file throws Apps aren't loaded yet error

Created on 5 Nov 2020  路  3Comments  路  Source: django/channels

Hi there again,

Okei I don't know anymore, am I doing something very wrong or there is something that I can't find in docs.

Let's say my asgi.py looks like this:

import os

from django.urls import re_path
from django.core.asgi import get_asgi_application

from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from channels.auth import AuthMiddlewareStack

from apps.logs.ws.routing import http_urlpatterns as logs_http_urlpatterns
from apps.support.ws.routing import websocket_urlpatterns as support_websocket_urlpatterns


os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')

application = ProtocolTypeRouter({
    "http": AuthMiddlewareStack(
        URLRouter(
            logs_http_urlpatterns
            + [re_path(r"", get_asgi_application())],
        )
    ),
    "websocket": AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter(
                support_websocket_urlpatterns
            )
        )
    ),
})

Using Channels ver. 3.0.1 and using manage.py runserver command, everything works fine and works.

Then I started to deploy changes to production and discovered problems in the staging phase.

I tested that with Daphne and Uvicorn ASGI-s and both throw the same error.

web_1          | Traceback (most recent call last):
web_1          |   File "/usr/local/lib/python3.9/runpy.py", line 197, in _run_module_as_main
web_1          |     return _run_code(code, main_globals, None,
web_1          |   File "/usr/local/lib/python3.9/runpy.py", line 87, in _run_code
web_1          |     exec(code, run_globals)
web_1          |   File "/usr/local/lib/python3.9/site-packages/daphne/__main__.py", line 3, in <module>
web_1          |     CommandLineInterface.entrypoint()
web_1          |   File "/usr/local/lib/python3.9/site-packages/daphne/cli.py", line 170, in entrypoint
web_1          |     cls().run(sys.argv[1:])
web_1          |   File "/usr/local/lib/python3.9/site-packages/daphne/cli.py", line 232, in run
web_1          |     application = import_by_path(args.application)
web_1          |   File "/usr/local/lib/python3.9/site-packages/daphne/utils.py", line 12, in import_by_path
web_1          |     target = importlib.import_module(module_path)
web_1          |   File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module
web_1          |     return _bootstrap._gcd_import(name[level:], package, level)
web_1          |   File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
web_1          |   File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
web_1          |   File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
web_1          |   File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
web_1          |   File "<frozen importlib._bootstrap_external>", line 790, in exec_module
web_1          |   File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
web_1          |   File "./config/asgi.py", line 12, in <module>
web_1          |     from channels.auth import AuthMiddlewareStack
web_1          |   File "/usr/local/lib/python3.9/site-packages/channels/auth.py", line 12, in <module>
web_1          |     from django.contrib.auth.models import AnonymousUser
web_1          |   File "/usr/local/lib/python3.9/site-packages/django/contrib/auth/models.py", line 2, in <module>
web_1          |     from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
web_1          |   File "/usr/local/lib/python3.9/site-packages/django/contrib/auth/base_user.py", line 48, in <module>
web_1          |     class AbstractBaseUser(models.Model):
web_1          |   File "/usr/local/lib/python3.9/site-packages/django/db/models/base.py", line 108, in __new__
web_1          |     app_config = apps.get_containing_app_config(module)
web_1          |   File "/usr/local/lib/python3.9/site-packages/django/apps/registry.py", line 253, in get_containing_app_config
web_1          |     self.check_apps_ready()
web_1          |   File "/usr/local/lib/python3.9/site-packages/django/apps/registry.py", line 136, in check_apps_ready
web_1          |     raise AppRegistryNotReady("Apps aren't loaded yet.")
web_1          | django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
dev_web_1 exited with code 1
Aborting on container exit...

Process finished with exit code 1

Commands i used:
daphne config.asgi:application -b 0.0.0.0
uvicorn config.asgi:application --host 0.0.0.0

As I can see, there are problems in the asgi.py file.
AuthMiddlewareStack is imported before apps are ready.
Even if I removed AuthMiddlewareStack, then it throws an error in URL patterns, because in those files, there are consumers, and they include models.

In docs:
https://channels.readthedocs.io/en/stable/deploying.html#configuring-the-asgi-application

There exist AuthMiddlewareStack and some consumers, which means I guess that should throw an error too when we try to deploy with the asgi.py file.

Workaround what I found is that I will add code below to the top of the asgi.py file:

import django

django.setup()

Then everything works again because apps are loaded before other modules are imported.
I don't know is that correct approach or I'm doing something very wrong?
If that is fine, then maybe we should update docs, so other people don't run into the same issues.

OS: docker - python:3.9.0-slim

Dependencies:
channels==3.0.1
daphne==3.0.0
uvicorn[standard]==0.12.2
Django==3.1.3
channels_redis==3.1.0
...

Most helpful comment

Thanks of the follow-up. It may be that we need to document this. 馃

All 3 comments

Hi @KStenK.

Calling django.setup() early here is sensible. It's done by get_asgi_application(), but if we're going to trigger AppRegistryNotReady then we might have to advise moving that up.

from django.core.asgi import get_asgi_application

django_asgi_app = get_asgi_application()
...

Thanks, @carltongibson,

import os

from django.urls import re_path
from django.core.asgi import get_asgi_application

django_asgi_app = get_asgi_application()

from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from channels.auth import AuthMiddlewareStack

...

application = ProtocolTypeRouter({
    "http": AuthMiddlewareStack(
        URLRouter(
            logs_http_urlpatterns
            + [re_path(r"", django_asgi_app)],
        )
...

This way everything works fine.

Thanks of the follow-up. It may be that we need to document this. 馃

Was this page helpful?
0 / 5 - 0 ratings