Opened 3 years ago

Closed 3 years ago

#21676 closed New feature (fixed)

Provide a way to run code when Django starts

Reported by: Aymeric Augustin Owned by: nobody
Component: Core (Other) Version: master
Severity: Normal Keywords: app-loading
Cc: django@…, mmitar@… Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Aymeric Augustin)

This ticket is a subset of #3591 which can be dealt with independently. The following text summarizes discussions I've had with other core devs, mostly jezdez and ptone.

Nowadays Django has two common entry points:

1) wsgi.py, which calls django.core.wsgi.get_wsgi_application(). This triggers a few imports in Django, but not many, and it doesn’t import the project. As a consequence, settings are configured and the app registry is populated while serving the first request. get_wsgi_application() was introduced with the idea that it would be used to populate the app registry in the future.

2) manage.py, which calls django.core.management.execute_from_command_line(). Unless requires_model_validation has been set to False, this will quickly import and validate models, thus populating the app cache.

In addition, there’s an interesting entry point, or rather lack thereof:

3) DJANGO_SETTING_MODULE=myproject.settings python. It’s part of Django’s purity and we certainly want to preserve this ability.

There’s no way we can run an implement an initialization sequence in this third use case, so I’m leaving it aside. For comparison, with Rails, you can’t just start irb, you have to use script/console. The initialization process is daunting: http://guides.rubyonrails.org/initialization.html

So the most realistic option is to trigger startup code from get_wsgi_application() and execute_from_command_line(). The startup code would:

  • call apps.populate_apps() — rather than apps.populate_models()
  • for each application, call a conventional method on its configuration object, for instance AppConfig.startup()

It could be useful to provide a way to run code once models are loaded, probably with a second conventional method. (I'd like to avoid signals entirely.)

The documentation recommends defining the app config class in an app submodule. That avoids import issues that may happen when putting initialization code in the app's __init__.py. In the worst case, imports can be moved inside the startup() method.

This technique doesn't required changing manage.py and wsgi.py, which is important.

Change History (11)

comment:1 Changed 3 years ago by Aymeric Augustin

Summary: Provide a way to run code when a Django application startsProvide a way to run code when Django starts

comment:2 Changed 3 years ago by Aymeric Augustin

Description: modified (diff)

comment:4 Changed 3 years ago by Aymeric Augustin

In terms of API, jezdez and I just discussed the following:

  • AppConfig.apps_ready() runs after apps are loaded; by default it's a noop.
  • AppConfig.models_ready() runs after models are loaded; by default it calls AppConfig.ready() which is a noop.

Simple API: implement AppConfig.ready() (or .startup(), .configure()... The name must mean "just put your code there")

Advanced API: override .apps_ready() and .models_ready() (and don't forget to call super(), to ensure .ready() gets called eventually)

comment:5 Changed 3 years ago by Chris Streeter

Cc: django@… added

comment:6 Changed 3 years ago by Mitar

Cc: mmitar@… added

comment:7 Changed 3 years ago by Aymeric Augustin

Triage Stage: UnreviewedAccepted

comment:8 Changed 3 years ago by Aymeric Augustin

Has patch: set
Patch needs improvement: set

comment:9 Changed 3 years ago by Aymeric Augustin

Patch needs improvement: unset
Triage Stage: AcceptedReady for checkin

Pull request is ready. Waiting for feedback on the mailing list.

comment:10 Changed 3 years ago by Aymeric Augustin <aymeric.augustin@…>

In 0d2c8ff2be733c7cc83a023bbafe0258faa5603c:

Populated the app registry earlier at startup.

Refs #1796, #21676.

comment:11 Changed 3 years ago by Aymeric Augustin

Resolution: fixed
Status: newclosed
Note: See TracTickets for help on using tickets.
Back to Top