Where to put Django startup code?
Asked Answered
L

6

56

I'd like to have these lines of code executed on server startup (both development and production):

from django.core import management
management.call_command('syncdb', interactive=False)

Putting it in settings.py doesn't work, as it requires the settings to be loaded already.

Putting them in a view and accessing that view externally doesn't work either, as there are some middlewares that use the database and those will fail and not let me access the view.

Putting them in a middleware would work, but that would get called each time my app is accessed. An possible solution might be to create a middleware that does all the job and then removes itself from MIDDLEWARE_CLASSES so it's not called anymore. Can I do that without too much monkey-patching?

Lidalidah answered 6/5, 2010 at 13:18 Comment(0)
L
61

Write middleware that does this in __init__ and afterwards raise django.core.exceptions.MiddlewareNotUsed from the __init__, django will remove it for all requests :). __init__ is called at startup by the way, not at the first request, so it won't block your first user.

There is talk about adding a startup signal, but that won't be available soon (a major problem for example is when this signal should be sent)

Related Ticket: https://code.djangoproject.com/ticket/13024

Update: Django 1.7 includes support for this. (Documentation, as linked by the ticket)

Loxodromic answered 6/5, 2010 at 13:30 Comment(10)
According to #6792411, this can be problematic. It is suggested there to put the start-up function in urls.pyHunsinger
Both middleware and urls.py do not run the startup code for management commands. Is there a better option?Overhang
I am still getting multiple startup calls (printing random string, prints it multiple times)Fluorometer
@krizajB: note that this answer was written with django 1.0 or 1.1 in mind, there are currently better solutions available and maybe this solution is even not valid anymore in django 1.4. I'd update the answer with more recent techniques, but I've not done much django development recently so if someone knows a better modern technique, feel free to add it.Loxodromic
Been googling whole day, couldn't find anything that would work .. At least not for my case :(Fluorometer
This doesn't seem to work in python 1.4... Any recommendations/workarounds?Brigham
As suggested by Michael, that seems to be the best solution at the moment (Django 1.4). There seems to be an update which isn't yet merged to the main branch, see github.com/ptone/django/blob/… . Oh, also be sure if you are using mod_wsgi to set it to deamon mode and if needed run it as a single process. See more here: code.google.com/p/modwsgi/wiki/IntegrationWithDjango keyword: WSGIDaemonProcess.Fluorometer
Found this question working from the other direction in 1.7 - ie: DB calls from AppConfig.ready() can have some really awkward side-effects. Specific use case is hooking up dynamic Models based on DB contents, so when tests are run, one can very quickly run into issues with missing tables. The middleware workaround may work best here, as it will only be called on the first request - ie: when we're on the right DB. We can then hook up something on the testing side to ensure that the init code gets called there too.Radie
Note that the linked solution for Django 1.7 explicitly warns against running any SQL (which appears to have been the original question).Unwinking
Again, how to do that - write the code to be executed on startup of the App?Blinders
P
11

In Django 1.7+ if you want to run a startup code and,

1. Avoid running it in migrate, makemigrations, shell sessions, ...

2. Avoid running it twice or more

A solution would be:

file: myapp/apps.py

from django.apps import AppConfig

def startup():
    # startup code goes here

class MyAppConfig(AppConfig):
    name = 'myapp'
    verbose_name = "My Application"
    def ready(self):
        import os
        if os.environ.get('RUN_MAIN'):
            startup()

file: myapp/__init__.py

default_app_config = 'myapp.apps.MyAppConfig'

This post is using suggestions from @Pykler and @bdoering

Propman answered 21/11, 2018 at 16:1 Comment(2)
who will set the environment varianle RUN_MAIN?Preconize
@Preconize apparently django does at runtimePropman
C
4

If you were using Apache/mod_wsgi for both, use the WSGI script file described in:

http://blog.dscpl.com.au/2010/03/improved-wsgi-script-for-use-with.html

Add what you need after language translations are activated.

Thus:

import sys

sys.path.insert(0, '/usr/local/django/mysite')

import settings

import django.core.management
django.core.management.setup_environ(settings)
utility = django.core.management.ManagementUtility()
command = utility.fetch_command('runserver')

command.validate()

import django.conf
import django.utils

django.utils.translation.activate(django.conf.settings.LANGUAGE_CODE)

# Your line here.
django.core.management.call_command('syncdb', interactive=False)

import django.core.handlers.wsgi

application = django.core.handlers.wsgi.WSGIHandler()
Confirmation answered 8/5, 2010 at 12:38 Comment(1)
That'd look a little different as I use twod.wsgi. Also, I'd like to do this on the dev server too. And I wouldn't go as low level as apache. The idea is that I might not want to give the app developer ssh access to the server, but let him use the django admin by putting it in his startup code.Lidalidah
A
3

You can create a custom command and write your code in the handle function. details here https://docs.djangoproject.com/en/dev/howto/custom-management-commands/

Then you can create a startup script that runs the django server then executes your new custom command.

Arundel answered 28/8, 2011 at 10:55 Comment(2)
This is a bad choice at it relies on a wrapper script around the server startup. What if someone forgets to start the server without the script?Loxodromic
I agree with @KillianDS. Still, it is a possibility. But then you'd have to use your wrapper as an entry point to the server, which may be any platform, mod_wsgi, app engine, tornado, etc.Lidalidah
T
1

If you are using mod_wsgi you can put it in the wsgi start app

Trovillion answered 7/5, 2010 at 15:1 Comment(0)
M
0

Here is how I work around the missing startup signal for Django: https://github.com/lsaffre/djangosite/blob/master/djangosite/models.py The code that is being called there is specific to my djangosite project, but the trick to get it called by writing a special app (based on an idea by Ross McFarland) should work for other environments. Luc

Mowery answered 26/12, 2013 at 16:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.