55

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?

Attila O.
  • 15,659
  • 11
  • 54
  • 84

6 Answers6

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)

Nick Merrill
  • 1,242
  • 1
  • 14
  • 20
KillianDS
  • 16,936
  • 4
  • 61
  • 70
  • 2
    According to http://stackoverflow.com/questions/6791911/execute-code-when-django-starts-once-only, this can be problematic. It is suggested there to put the start-up function in urls.py – Michael Dec 01 '11 at 22:20
  • 2
    Both middleware and urls.py do not run the startup code for management commands. Is there a better option? – Andrei Feb 13 '12 at 12:13
  • I am still getting multiple startup calls (printing random string, prints it multiple times) – krizajb Sep 13 '12 at 10:35
  • 2
    @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. – KillianDS Sep 13 '12 at 10:41
  • Been googling whole day, couldn't find anything that would work .. At least not for my case :( – krizajb Sep 13 '12 at 13:33
  • 2
    This doesn't seem to work in python 1.4... Any recommendations/workarounds? – rui Nov 07 '12 at 19:39
  • 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 https://github.com/ptone/django/blob/02d0fa3f14d1ad72958f403f16bdd662d200a558/django/apps/cache.py#L208 . 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: http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango keyword: WSGIDaemonProcess. – krizajb Jan 02 '13 at 19:26
  • 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. – tr00st Jul 22 '14 at 10:16
  • Note that the linked solution for Django 1.7 explicitly warns against running any SQL (which appears to have been the original question). – Klaas van Schelven Feb 13 '15 at 14:26
  • Again, how to do that - write the code to be executed on startup of the App? – Incerteza Mar 06 '15 at 10:11
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

Joseph Bani
  • 644
  • 6
  • 16
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()
Graham Dumpleton
  • 57,726
  • 6
  • 119
  • 134
  • 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. – Attila O. May 10 '10 at 14:41
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.

Emam
  • 620
  • 1
  • 6
  • 12
  • 1
    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? – KillianDS Aug 29 '11 at 11:09
  • I agree with @[KillianDS](http://stackoverflow.com/users/243870/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. – Attila O. Aug 29 '11 at 14:47
1

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

alex
  • 479,566
  • 201
  • 878
  • 984
Aviah Laor
  • 3,620
  • 2
  • 22
  • 27
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

Luc Saffre
  • 96
  • 2