skip to content

In Django, what is the difference between the django-admin utility and a project's manage.py, and when do you use each?

level: juniorimportance: should knowfreq 48%

answer

  1. same ManagementUtility underneath
  2. one environment variable set for you
  3. os.environ.setdefault, not assignment
  4. startproject before any project exists
  5. --settings for switching modules

basics

~10 s

Both run the same command machinery. manage.py is generated per project and sets DJANGO_SETTINGS_MODULE to that project's settings; django-admin is installed with Django and needs settings supplied, so it suits startproject and switching settings.

solid answer

~30 s

`django-admin` is the console script pip installs with Django; `manage.py` is a small file `startproject` writes into every project. Both call `execute_from_command_line()`, so the commands and options are identical, and `python -m django` is a third spelling of `django-admin`. The difference is configuration: `manage.py` runs `os.environ.setdefault('DJANGO_SETTINGS_MODULE', '<project>.settings')`, and because it sits in the project root that directory is on the import path. `django-admin` sets nothing, so without `DJANGO_SETTINGS_MODULE` or `--settings` it only knows Django's core commands. Use `manage.py` day to day inside one project; use `django-admin` for `startproject`, or when you deliberately switch between settings modules.

code

bash · 5 lines
bash
django-admin startproject shopsite .              # no manage.py exists yet
python manage.py expire_coupons                   # settings come from manage.py's setdefault
DJANGO_SETTINGS_MODULE=shopsite.settings.staging python manage.py migrate --plan
django-admin expire_coupons --settings=shopsite.settings.staging
python -m django version

go deeper

for a junior

Recall that both run the same commands, manage.py sets DJANGO_SETTINGS_MODULE to the project's settings, and django-admin startproject creates the project in the first place.

for a middle

Explain setdefault versus assignment, why bare django-admin cannot find app commands, and how --settings and --pythonpath fill the gap.

for a senior

Show how you run commands in containers and CI by exporting DJANGO_SETTINGS_MODULE explicitly, and how you diagnose a command that silently used the wrong settings module.

for a principal

Treat the command entry point as part of the deployment contract: one documented way to select settings per environment, so scripts and humans cannot drift apart.

## Two entry points, one machinery Django ships a command-line layer called the **management utility** (`django.core.management.ManagementUtility`). Every way of running a command ends in `execute_from_command_line()`: - **`django-admin`** is a console script declared in Django's packaging, so installing Django into a virtual environment puts it on your `PATH`. - **`manage.py`** is a short Python file that `django-admin startproject` generates in the project root. - **`python -m django`** runs `django/__main__.py`, which is equivalent to `django-admin`. The list of commands, their options (`--verbosity`, `--settings`, `--pythonpath`, `--traceback`, `--no-color`, `--force-color`, `--skip-checks` where checks apply) and their behaviour are the same whichever entry point you use. ## What manage.py adds Open a generated `manage.py` and it does two useful things before handing over: 1. It calls `os.environ.setdefault("DJANGO_SETTINGS_MODULE", "<project>.settings")`. Because this is `setdefault`, an already-exported `DJANGO_SETTINGS_MODULE` **wins** over the default — a detail that surprises people who expect `manage.py` to force its own settings. 2. Because it is a script in the project root, Python puts that directory at the front of the import path, so `<project>.settings` and your apps import without extra configuration. It also gives a helpful `ImportError` message if Django itself is not importable, which usually means the virtual environment is not active. ## What django-admin needs from you `django-admin` configures nothing. The consequences are concrete: - Commands that do not need a project, above all **`startproject`** (and `startapp`, `version`, `help`), work anywhere — that is why you create a project with `django-admin startproject`, not with a `manage.py` that does not exist yet. - For anything that touches your project, you supply the settings: export `DJANGO_SETTINGS_MODULE`, or pass `--settings=config.settings.staging`, and add `--pythonpath` if the project is not importable from the current directory. - Without settings, command discovery only sees Django's **core** commands. App commands such as `createsuperuser` (from `django.contrib.auth`) or your own `expire_coupons` are found by scanning `INSTALLED_APPS`, which requires configured settings. The result is `No Django settings specified.` followed by `Unknown command: 'expire_coupons'`. ## How commands are discovered Whichever entry point you use, the command list is built the same way, and knowing it explains most "command not found" puzzles: 1. Django registers its **core commands** from `django/core/management/commands/`. 2. If settings are configured, it walks `INSTALLED_APPS` **in reverse** and adds every module in each app's `management/commands/` directory whose name does not start with an underscore. 3. Because later registrations overwrite earlier ones and the walk is reversed, an app listed **earlier** in `INSTALLED_APPS` wins a name clash, and any app can override a core command — `django.contrib.staticfiles` replaces `runserver` this way. 4. The resulting mapping is cached for the life of the process. So a missing command means one of three things: settings were not configured, the app is not installed, or the module name starts with an underscore. ## Choosing between them | Situation | Use | Why | |---|---|---| | Creating a new project | `django-admin startproject` | No project, so no `manage.py` yet | | Daily work inside one project | `python manage.py <command>` | Settings and import path set for you | | Running a command against another settings module | either, with `--settings=` | The flag overrides the environment for that run | | Scripts or containers that already export `DJANGO_SETTINGS_MODULE` | either | `setdefault` in `manage.py` defers to the exported value | | Running Django with no console script on `PATH` | `python -m django` | Same machinery through the interpreter | ## Common confusions - **"django-admin" is not the admin site.** The command-line utility and `django.contrib.admin` share a word and nothing else. - **Editing `manage.py` to hard-code production settings** is a smell: the environment variable or `--settings` is the switch, and per-environment settings layout is a configuration concern of its own. - **Running `manage.py` from another directory** still works when you call it by path, because Python puts the script's own directory on the import path, not the current working directory. - **`--settings` is not a merge.** It names one settings module for that run; whatever that module imports is what you get.

  • Running django-admin expire_coupons prints 'Unknown command'; the same command works with manage.py. Why?
    Django only discovers app-level commands by scanning `INSTALLED_APPS`, which needs configured settings. `manage.py` sets `DJANGO_SETTINGS_MODULE` for you; bare `django-admin` does not, so it sees only core commands and prints `No Django settings specified.` before `Unknown command`. Export the variable or pass `--settings` (plus `--pythonpath` if the project is not importable).
  • If DJANGO_SETTINGS_MODULE is exported in the shell, which settings does manage.py use?
    The exported one. The generated `manage.py` uses `os.environ.setdefault`, which only fills the variable when it is absent. That is convenient for containers and CI that export the variable, but it also explains 'wrong settings' surprises when a stale export lingers in a developer's shell.

saying these in an interview costs you the question

  • django-admin is the command that opens the Django admin site
  • manage.py and django-admin support different sets of commands
  • manage.py always overrides an exported DJANGO_SETTINGS_MODULE
  • You need manage.py to create a new project
  • --settings merges the named module into the default settings