Why is run called twice in the Django dev server?
Asked Answered
A

2

29

I want to make the Django development server do something before it starts running. To do this, I created a new app, added it to the top of INSTALLED_APPS, and then created a management/commands/runserver.py file in the app with the following code:

from django.contrib.staticfiles.management.commands.runserver import Command as RunserverCommand
class Command(RunserverCommand):
    def run(self, *args, **options):
        self.stdout.write('About to start running on ' + self.addr)
        super(Command, self).run(*args, **options)

(The thing I actually want to do is more complicated than writing one line to stdout, of course, but this is the simplest example that demonstrates the problem. The reason I override run, rather than handle or some other method, is because I need self.addr to already be set when this code runs.)

When I run ./manage.py runserver, the line "About to start running on 127.0.0.1" appears not once, but twice before the server starts running. Why is this happening and what can be done about it?

Amundson answered 12/2, 2015 at 23:27 Comment(3)
You most likely do not want to edit the run command, have a look at Django startup hook functionality mentioned in this answer. https://mcmap.net/q/50108/-execute-code-when-django-starts-once-only The auto-reloading feature of django runserver, will run things twice but it is harmless and only used in dev. You shouldnt be using runserver in production, in production the startup code will only run once.Sharpfreeze
This was for local development only, actually.Amundson
In that case you shouldn't worry much that it runs twice, since it is really just dev. You want to write your code in such a way that it works in production as well, editing the run command won't work when you finally deploy it under uwsgi or gunicorn.Sharpfreeze
A
41

The auto-reloader process turned out to be the culprit; turns out the autoreload process gets the same arguments, and goes through the same initialization process, as the original. The solution was to have the pre-server code execute only if it's not running in the process spawned by the autoreloader, which can be detected through an environment variable:

import os
from django.contrib.staticfiles.management.commands.runserver import Command as RunserverCommand
class Command(RunserverCommand):
    def run(self, *args, **options):
        if os.environ.get('RUN_MAIN') != 'true':
            self.stdout.write('About to start running on ' + self.addr)
        super(Command, self).run(*args, **options)
Amundson answered 13/2, 2015 at 16:23 Comment(0)
T
30

The local development server runs a separate process for the auto-reloader. You can turn off the auto-reload process by passing the --noreload flag.

python manage.py runserver --noreload
Tetany answered 12/2, 2015 at 23:45 Comment(1)
This was a perfect way to validate what was happening. Thanks!Labdanum

© 2022 - 2024 — McMap. All rights reserved.