Apps, settings and project structure
Django projects are built from apps, and the way you divide a project into apps — and configure it through settings — is a structural decision you make early and live with for a long time. Get the division sensible and the codebase stays navigable as it grows; get it wrong and everything ends up in one giant module nobody can find their way around. This lesson explains the project/app distinction, how settings actually work, and how to structure Nidaan so it scales.
Project versus app — the distinction that confuses everyone first
Django has two levels, and the names are unfortunately similar:
- A project is the whole thing — one deployable website. You have exactly one. It is the
configpackage (settings, root URLs) plus everything around it. Nidaan is a project. - An app is a self-contained module within the project that does one job. A project is made of several apps. You create them as you go.
The rule of thumb: an app should do one thing, and be conceivably reusable. Django's own built-ins are
apps — django.contrib.auth is the authentication app, django.contrib.admin is the admin app. For
Nidaan, natural apps are patients (patient records), appointments (scheduling), reports (diagnostic
test results), and perhaps billing. Each owns its models, views, URLs and templates for its slice of the
domain.
Create one with:
python manage.py startapp appointments
This scaffolds an appointments/ directory with the standard files — and creating apps by feature, not by
technical layer, is the decision that matters, which we come to below.
What is in an app
startapp appointments generates:
appointments/
__init__.py
admin.py ← register models with the admin here
apps.py ← the app's configuration class
models.py ← this app's database models
views.py ← this app's views
migrations/ ← this app's schema-change history
tests.py ← this app's tests
You typically add an app-level urls.py yourself (as we did for patients). Notice every file is this
app's — the app is a coherent bundle of one feature's data, logic, URLs and tests. That locality is the
point: to understand appointments, you look in one directory, not scattered across the project.
Registering an app: INSTALLED_APPS
An app does nothing until the project knows about it. You register it in config/settings.py:
# config/settings.py
INSTALLED_APPS = [
"django.contrib.admin", # the admin site
"django.contrib.auth", # authentication
"django.contrib.contenttypes",
"django.contrib.sessions", # sessions
"django.contrib.messages", # the messages framework
"django.contrib.staticfiles", # static file handling
# your apps:
"patients",
"appointments",
]
The built-in django.contrib.* apps are why you got auth, sessions and the admin for free — they are apps,
registered by default. Your app must appear in INSTALLED_APPS or Django ignores it — its models will
not get tables, its admin registrations will not show, its templates will not be found. "Why isn't my model
migrating?" is, nine times out of ten, an app missing from this list.
Settings: it is just a Python module
config/settings.py is not a special format — it is a plain Python module of module-level variables that
Django reads. That is powerful (you can compute values, import, branch on environment) and a responsibility
(a syntax error here breaks everything). The settings you meet first:
DEBUG—Truein development (detailed error pages, auto-served static files), and must beFalsein production (detailed errors leak your code and configuration to attackers). This is the single most important setting to get right for a live site.ALLOWED_HOSTS— the domain names your site is allowed to serve. Empty is fine withDEBUG=True; in production it must list your real domains, or Django refuses the request (a deliberate protection).SECRET_KEY— a random string Django uses for cryptographic signing (sessions, password resets, CSRF). It must be secret and unique; a leaked key is a serious breach. It belongs in an environment variable, never in the committed code — the deployment module returns to this.DATABASES— the database connection. SQLite by default in development; PostgreSQL in production.INSTALLED_APPS,MIDDLEWARE,TEMPLATES— the wiring of apps, request layers and the template engine.
The generated settings.py ships with DEBUG=True and an insecure SECRET_KEY right in the file, clearly
marked. That is fine for the first day and dangerous for production — managing settings safely across
environments is important enough that it has its own lesson later.
Structure apps by feature, not by layer — the decision that ages well
The most consequential structural choice, and the one beginners most often get backwards: divide apps by feature (a slice of the domain), not by technical layer.
- Wrong (by layer): a
modelsapp holding all models, aviewsapp holding all views, atemplatesapp. This feels tidy for a week and becomes unnavigable fast — to change appointments you touch three apps, and no app means anything on its own. - Right (by feature): a
patientsapp with patients' models, views, URLs and templates together; anappointmentsapp with appointments'; areportsapp with reports'. To change a feature, you work in one directory. Each app is understandable, testable and, in principle, reusable in isolation.
This is the same locality principle as the app layout itself, applied one level up. A new engineer joining
Nidaan can be told "billing lives in the billing app" and find everything about billing in one place —
which is exactly what you want a codebase to allow, and exactly what "by layer" destroys.
Do not over-fragment either: a very small project can start as one or two apps and split later. The goal is apps that each represent a real, coherent piece of the domain — not one giant app, and not fifty tiny ones.
Check your work
Project versus app. A project is the whole deployable site (one, the config package); an app is a
self-contained feature module within it (many). Nidaan is the project; patients/appointments are apps.
What an app contains. Its own models.py, views.py, admin.py, migrations/, tests.py (and
usually a urls.py you add) — one feature's data, logic and tests bundled together.
What INSTALLED_APPS does. Registers apps with the project; an unregistered app is ignored (no tables,
no admin, no templates) — the usual cause of "my model won't migrate".
Why the django.contrib.* apps matter. Auth, sessions, admin and more are themselves apps, registered
by default — that is why they come free.
The settings that matter first. DEBUG (False in production, non-negotiable), ALLOWED_HOSTS,
SECRET_KEY (secret, from the environment), DATABASES, and the app/middleware/template wiring.
By feature, not by layer. Divide apps into domain slices (patients, appointments) so each feature
lives in one directory — not into models/views/templates apps, which scatter every change.
Practice
- In the
nidaanproject, create anappointmentsapp and register it inINSTALLED_APPS. Runpython manage.py check. - Create an app but forget to register it; try to use it and observe what fails. Register it and confirm
the fix — so the role of
INSTALLED_APPSis concrete. - Open
config/settings.pyand findDEBUG,ALLOWED_HOSTS,SECRET_KEY,INSTALLED_APPS. Read the comment aboveSECRET_KEY. - Set
DEBUG = FalsewithALLOWED_HOSTS = [], restart the server, load a page, and read the error. RestoreDEBUG = True. You have seen why the pairing matters. - Sketch Nidaan as apps: list four to six apps and, for each, one sentence on what it owns. Justify why you did not put it all in one app.
- Look at Django's
django.contribpackages and note three that are apps you already benefit from.
Official documentation
- Django — Applications — What an app is and its configuration.
- Django — Settings reference — Every setting, including
DEBUG,ALLOWED_HOSTS,SECRET_KEY. - Django — Writing your first app, part 1 —
startappandINSTALLED_APPSin context.
Next: defining models and fields — the data layer.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship