Execute code when Django starts ONCE only?

前端 未结 7 1387
终归单人心
终归单人心 2020-11-22 13:55

I\'m writing a Django Middleware class that I want to execute only once at startup, to initialise some other arbritary code. I\'ve followed the very nice solution posted by

相关标签:
7条回答
  • 2020-11-22 14:31

    Note that you cannot reliability connect to the database or interact with models inside the AppConfig.ready function (see the warning in the docs).

    If you need to interact with the database in your start-up code, one possibility is to use the connection_created signal to execute initialization code upon connection to the database.

    from django.dispatch import receiver
    from django.db.backends.signals import connection_created
    
    @receiver(connection_created)
    def my_receiver(connection, **kwargs):
        with connection.cursor() as cursor:
            # do something to the database
    

    Obviously, this solution is for running code once per database connection, not once per project start. So you'll want a sensible value for the CONN_MAX_AGE setting so you aren't re-running the initialization code on every request. Also note that the development server ignores CONN_MAX_AGE, so you WILL run the code once per request in development.

    99% of the time this is a bad idea - database initialization code should go in migrations - but there are some use cases where you can't avoid late initialization and the caveats above are acceptable.

    0 讨论(0)
  • 2020-11-22 14:36

    Update: Django 1.7 now has a hook for this

    file: myapp/apps.py

    from django.apps import AppConfig
    class MyAppConfig(AppConfig):
        name = 'myapp'
        verbose_name = "My Application"
        def ready(self):
            pass # startup code here
    

    file: myapp/__init__.py

    default_app_config = 'myapp.apps.MyAppConfig'
    

    For Django < 1.7

    The number one answer does not seem to work anymore, urls.py is loaded upon first request.

    What has worked lately is to put the startup code in any one of your INSTALLED_APPS init.py e.g. myapp/__init__.py

    def startup():
        pass # load a big thing
    
    startup()
    

    When using ./manage.py runserver ... this gets executed twice, but that is because runserver has some tricks to validate the models first etc ... normal deployments or even when runserver auto reloads, this is only executed once.

    0 讨论(0)
  • 2020-11-22 14:36

    if you want print "hello world" once time when you run server, put print ("hello world") out of class StartupMiddleware

    from django.core.exceptions import MiddlewareNotUsed
    from django.conf import settings
    
    class StartupMiddleware(object):
        def __init__(self):
            #print "Hello world"
            raise MiddlewareNotUsed('Startup complete')
    
    print "Hello world"
    
    0 讨论(0)
  • 2020-11-22 14:38

    Update from Pykler's answer below: Django 1.7 now has a hook for this


    Don't do it this way.

    You don't want "middleware" for a one-time startup thing.

    You want to execute code in the top-level urls.py. That module is imported and executed once.

    urls.py

    from django.confs.urls.defaults import *
    from my_app import one_time_startup
    
    urlpatterns = ...
    
    one_time_startup()
    
    0 讨论(0)
  • 2020-11-22 14:38

    If it helps someone, in addition to pykler's answer, "--noreload" option prevents runserver from executing command on startup twice:

    python manage.py runserver --noreload
    

    But that command won't reload runserver after other code's changes as well.

    0 讨论(0)
  • 2020-11-22 14:45

    This question is well-answered in the blog post Entry point hook for Django projects, which will work for Django >= 1.4.

    Basically, you can use <project>/wsgi.py to do that, and it will be run only once, when the server starts, but not when you run commands or import a particular module.

    import os
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ project_name }}.settings")
    
    # Run startup code!
    ....
    
    from django.core.wsgi import get_wsgi_application
    application = get_wsgi_application()
    
    0 讨论(0)
提交回复
热议问题