skip to content

In a Django app, what is each of models.py, views.py, urls.py, admin.py, apps.py and tests.py for, and which does Django find by convention?

level: juniorimportance: should knowfreq 48%

answer

  1. some are discovered, some referenced
  2. app registry imports one module
  3. admin's ready() looks for another
  4. test runner's file pattern
  5. urls.py reached only through include()

basics

~10 s

models.py holds models, views.py view callables, urls.py URL patterns, admin.py admin registrations, apps.py the AppConfig, tests.py tests. Django finds models, apps, admin and test*.py modules by name; views and urls run only when referenced.

solid answer

~40 s

Each file has one job. `models.py` defines the app's models; the app registry imports the `models` submodule of every installed app at startup. `apps.py` holds the `AppConfig`; Django looks for an `apps` submodule when `INSTALLED_APPS` names the package. `admin.py` registers models with the admin; the default `AdminConfig.ready()` calls `autodiscover()`, which imports every installed app's `admin` module. `tests.py` is found by the test runner's default `test*.py` pattern. `views.py` is only a convention — Django never imports it by name; your `urls.py` does. And `urls.py` is not generated by `startapp` at all: you create it and wire it in with `include()` from the project URLconf that `ROOT_URLCONF` names.

go deeper

for a junior

Know each file's job and that you must create the app's urls.py yourself and include() it from the project URLconf.

for a middle

Explain which modules Django imports by name (models, apps, admin via autodiscover, test*.py) and why views.py runs only because urls.py imports it.

for a senior

Diagnose 'my code never runs' bugs from the import path: startup code in views.py, admin registrations in a non-admin module, a models package missing imports.

for a principal

Set layout conventions for a large codebase, such as models and views packages, so discovery rules still hold and new contributors know where code belongs.

## Why the file names matter A Django app is a Python package, and Django treats a few of its module names specially. Knowing **which files Django discovers by name** and **which run only because your code references them** explains most "why is my code not running?" questions from newcomers. Take a `recipes` app on a recipe-sharing site. ## What each file is for | File | Holds | How it gets imported | |---|---|---| | `models.py` | `Model` subclasses such as `Recipe` and `Ingredient` | the app registry imports the app's `models` submodule while populating | | `apps.py` | the app's `AppConfig` subclass | Django looks for an `apps` submodule when `INSTALLED_APPS` lists the package path | | `admin.py` | `admin.site.register(...)` calls and `ModelAdmin` classes | `django.contrib.admin`'s default `AdminConfig.ready()` runs `autodiscover()`, which imports each installed app's `admin` module | | `tests.py` | test cases | the test runner's default discovery pattern is `test*.py` | | `views.py` | view functions and classes | **not discovered** — imported by your URLconf | | `urls.py` | the app's `urlpatterns` list | **not discovered and not generated** — included from the root URLconf | ## Discovered by convention - **`models.py`** — while `django.setup()` populates the registry, each `AppConfig` imports its `models` submodule if it has one. A model defined in a module nothing imports is invisible to migrations and the admin. - **`apps.py`** — if the package has an `apps` submodule with exactly one `AppConfig` subclass (or one marked `default = True`), Django uses it automatically. - **`admin.py`** — only because of autodiscovery. If a project installs `django.contrib.admin.apps.SimpleAdminConfig` instead of the default, `admin.py` modules are **not** imported and registrations must be done by hand. - **`tests.py`** — `manage.py test` discovers files matching `test*.py`, so a `tests/` package with `test_models.py` works too, while a file called `recipe_checks.py` is silently skipped. ## Reached only by reference - **`views.py`** — the name is pure convention. Django only calls a view because a URL pattern points at it; you could split views into a `views/` package and nothing would break as long as `urls.py` imports from it. - **`urls.py`** — `startapp` creates `__init__.py`, `admin.py`, `apps.py`, `migrations/`, `models.py`, `tests.py` and `views.py`, but **no `urls.py`**. You create `recipes/urls.py` and connect it from the project URLconf: ```python # mysite/urls.py (the module ROOT_URLCONF names) from django.contrib import admin from django.urls import include, path urlpatterns = [ path("admin/", admin.site.urls), path("recipes/", include("recipes.urls")), ] ``` The request path therefore reaches app code in this order: the root URLconf, the app's `urls.py`, then a view from `views.py`, which uses models from `models.py` and typically a template. ## Typical mistakes 1. Writing `recipes/urls.py` but never adding `include("recipes.urls")` to the root URLconf — every recipe URL returns 404. 2. Registering models in a module called `admin_setup.py` — autodiscovery only imports modules named `admin`. 3. Putting signal receivers or startup code in `views.py` and wondering why it runs late or not at all — views are imported only when a URLconf that references them is loaded, not during app loading. 4. Naming a test module `checks.py` and seeing zero tests run. ## What an interviewer is listening for The junior answer lists each file's job. The better answer separates the **discovered** modules (`models`, `apps`, `admin`, `test*.py`) from the **referenced** ones (`views`, `urls`) and knows `startapp` does not create `urls.py`.

  • Can a Django app split models.py into a models/ package?
    Yes. Django imports the app's `models` submodule, so a `models/` package works as long as its `__init__.py` imports the model classes (or the modules defining them). A model in a file that nothing imports is not registered, so migrations and the admin will not see it.
  • What stops admin.py from being imported automatically?
    Autodiscovery comes from the default `AdminConfig.ready()` calling `admin.autodiscover()`. If `INSTALLED_APPS` lists `django.contrib.admin.apps.SimpleAdminConfig` instead, no `admin` modules are imported and you must register models explicitly, for example in a custom admin site module.

saying these in an interview costs you the question

  • Expects startapp to generate a urls.py for the new app
  • Believes Django imports views.py automatically at startup
  • Thinks any module name works for admin registrations
  • Assumes a test file with any name is discovered by manage.py test
  • Says models must live in a single file called models.py