When launching manage.py runserver through VS Code's Python debugger on
Windows, the console often shows each line twice. This happens because
manage.py starts two processes whenever Django's autoreloader is
enabled (the default in development):
Both processes print their startup diagnostics (e.g., "No changes detected" and migration checks), so you see duplicated lines before the server begins serving requests. Once the server is running, only the child process keeps printing new logs.
To avoid the duplication, disable the autoreloader when debugging:
.venv\Scripts\python.exe manage.py runserver --noreload
You can also set DJANGO_SUPPRESS_MIGRATION_CHECK=1 if you only want to
silence the repeated migration output while keeping the autoreloader active.
Django's autoreloader runs your module imports twice (once in the watcher and
once in the child server). If you need certain initialization to happen only
once, run it before runserver starts or gate it to the child process:
Add a pre-flight step in every launcher (PowerShell, Bash, VS Code) that runs the expensive checks once, then skip them in the subsequent server start:
powershell
.venv\Scripts\python.exe manage.py migrate --check
$env:DJANGO_SUPPRESS_MIGRATION_CHECK = "1"
.venv\Scripts\python.exe manage.py runserver
bash
.venv/bin/python manage.py migrate --check
export DJANGO_SUPPRESS_MIGRATION_CHECK=1
.venv/bin/python manage.py runserver
The first command ensures migrations are verified a single time; the environment variable suppresses the duplicated check when the watcher forks the child server.
For app-level initialization that runs during import or AppConfig.ready,
guard it so it only executes in the child server process:
```python import os
def ready(self): if os.environ.get("RUN_MAIN") != "true": return # skip watcher process perform_expensive_setup() ```
These patterns prevent double execution while keeping the autoreloader intact for code reloads during development.