Starting a project the right way
The first thing you do on a Django project shapes everything after it. Set it up carelessly and you fight your environment for weeks; set it up well and you barely think about it again. This lesson gets you from nothing to a running Django project — Nidaan, the clinic system we build across the course — the way a professional would, and explains why each step is what it is rather than having you copy commands blind.
Step 1: an isolated environment (never skip this)
Python projects must not share dependencies globally. Two projects will eventually need two different versions of the same package, and a global install makes that impossible — the classic "it works on my machine" mess. A virtual environment gives this project its own private set of packages:
mkdir nidaan && cd nidaan
python3 -m venv venv # create an isolated environment in ./venv
source venv/bin/activate # activate it (Windows: venv\Scripts\activate)
Once activated, your shell prompt shows (venv), and python and pip now refer to this project's
isolated copies. Everything you install lands here, not system-wide. Deactivate with deactivate when you
are done. Activate the venv every time you work on the project — forgetting is the single most common
beginner confusion ("why can't it find Django?" — because you installed it in the venv but are not in it).
Step 2: install Django, and pin what you install
Inside the activated environment:
pip install "Django>=5.1,<5.2"
Two deliberate choices. We install a pinned range, not a bare pip install django, so the version is
predictable — a project that silently upgrades its framework across a minor release is asking for surprise
breakage. And immediately, we record what we installed:
pip freeze > requirements.txt
requirements.txt lists every package and its exact version, so anyone (including future you, or the
deploy server) can recreate this environment with pip install -r requirements.txt. Commit
requirements.txt; never commit the venv/ directory — the venv is machine-specific and large, the
requirements file is the portable, reproducible record. This course uses Django 5.1.
Step 3: create the project — and the config convention
Now the command that scaffolds a Django project. Note the exact form, because the trailing dot matters:
django-admin startproject config .
This creates a Python package called config and a manage.py file in the current directory (the
.). Why config and the dot? The naive django-admin startproject nidaan creates a nidaan/nidaan/
nested folder — a confusing double-nesting where the outer and inner folders share a name. Naming the
settings package config and using . gives you a clean, flat layout where the repository is nidaan
and the settings package is unambiguously config. It is a widely-used convention for exactly this
reason. Your layout is now:
nidaan/
manage.py ← your command-line entry point for everything
config/
__init__.py
settings.py ← project configuration
urls.py ← the root URL map
wsgi.py ← entry point for traditional (sync) servers
asgi.py ← entry point for async servers
venv/ ← (not committed)
requirements.txt
manage.py is the tool you run for everything — starting the server, creating apps, running migrations,
opening a shell. config/settings.py is where the project is configured. config/urls.py maps URLs to
code. The two server files (wsgi.py, asgi.py) are entry points production servers use; you will not
touch them until deployment.
Step 4: initialise the database and run the server
Django comes with built-in apps (auth, sessions, admin) that need database tables. Create them, then start the development server:
python manage.py migrate # create the tables Django's built-in apps need
python manage.py runserver # start the development server on http://127.0.0.1:8000/
migrate applies Django's initial migrations to a database — by default a db.sqlite3 file created right
there, which is perfect for development (we move to PostgreSQL for production later). runserver starts a
lightweight development server. Visit http://127.0.0.1:8000/ and you will see Django's rocket-ship
welcome page — the confirmation that the install worked. That development server auto-reloads when you edit
code, so you leave it running while you work.
The development server is for development only. It is single-threaded, unoptimised, and explicitly not built to face the public internet — the deployment module covers the real server. Seeing "runserver" in a production deploy is a red flag.
Step 5: the .gitignore, before your first commit
Initialise git and, crucially, ignore the things that must never be committed:
git init
Create a .gitignore with at least:
venv/
db.sqlite3
__pycache__/
*.pyc
.env
Why each: venv/ is machine-specific and huge; db.sqlite3 is local data, not code; __pycache__/ and
*.pyc are compiled artefacts; and .env will later hold secrets (database passwords, the secret key) that
must never reach a repository. Getting .gitignore right before the first commit saves you from the
awkward job of scrubbing a committed secret from history later — a mistake that, for a real secret, means
rotating it. Set it up now, once.
The setup checklist
Every new Django project, in order: make a folder → create and activate a venv → install a pinned Django
and freeze requirements → startproject config . → migrate → runserver to confirm → git init with a
correct .gitignore. Do these seven and you have a clean, reproducible, source-controlled starting point.
Skip the venv or the .gitignore and you will pay for it — those two are where the pain lives.
Check your work
Why a virtual environment. It isolates this project's packages so versions do not collide with other projects or the system — and you must activate it every session.
Why pin Django and freeze requirements. A pinned range prevents surprise upgrades; requirements.txt
records exact versions so the environment is reproducible. Commit it; never commit venv/.
Why startproject config .. The . scaffolds into the current directory and naming the package
config avoids the confusing nidaan/nidaan/ double-nesting — a clean, conventional layout.
What manage.py is. The command-line entry point for everything — server, apps, migrations, shell.
What migrate and runserver do. migrate creates the tables Django's built-in apps need (default
SQLite in development); runserver starts the auto-reloading development server — not for production.
What must be in .gitignore from the start. venv/, db.sqlite3, __pycache__/, *.pyc, .env —
set before the first commit so a secret never reaches history.
Practice
- Create the
nidaanproject from scratch following all seven steps. Confirm the welcome page loads athttp://127.0.0.1:8000/. - Deactivate the venv and run
python -c "import django"; read theModuleNotFoundError. Reactivate and watch it succeed — proof of what the venv does. - Open
requirements.txtand read it. Delete the venv entirely, recreate it, and restore the environment withpip install -r requirements.txt— the reproducibility in action. - Run
django-admin startproject nidaan(no dot) in a throwaway folder and observe the double-nested layout, so you understand what theconfig .form avoids. - Commit the project. Run
git statusand confirmvenv/anddb.sqlite3do not appear — proof the.gitignoreworks. - Run
python manage.py runserver 8001and note you can choose the port; visit it.
Official documentation
- Django — Quick install guide — The official install steps.
- Django — Writing your first app, part 1 —
startproject,runserverand the project layout. - Python — venv — Virtual environments in depth.
- pip — Requirements files — The format
requirements.txtuses.
Next: how a request flows through Django.
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