From zero to a running Django app
In the last post we covered what Django is and why it’s worth learning. Now let’s actually install it, scaffold a project, and walk through every file the framework generates — so when something breaks later, you know exactly where to look.
By the end of this post you’ll have a working “Hello, World!” Django app running on localhost:8000.
Step 1: Create a virtual environment
Always isolate Python projects in their own virtual environment. This prevents the dependencies of one project from clobbering another, and keeps your global Python install clean.
mkdir django_conquered && cd django_conquered
python3 -m venv .venv
source .venv/bin/activate # macOS / Linux
# .venv\Scripts\activate # Windows PowerShell
You’ll know it worked when your shell prompt is prefixed with (.venv). If you prefer modern tooling, uv
and poetry
are both excellent alternatives — but venv is built in and good enough for getting started.
Step 2: Install Django
pip install --upgrade pip
pip install "django>=5.0,<6.0"
Pin to a major version so a future Django release doesn’t surprise you. Once installed, freeze your dependencies:
pip freeze > requirements.txt
Verify the install:
django-admin --version
Step 3: Create the project
django-admin startproject conquered .
The trailing . is important — it tells Django to scaffold into the current directory rather than creating an extra nested folder. Your tree should now look like this:
django_conquered/
├── .venv/
├── manage.py
├── requirements.txt
└── conquered/
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
Step 4: A tour of the generated files
Let’s go file by file. Understanding this structure now saves hours of confusion later.
manage.py
A thin wrapper around django-admin that knows about your project’s settings. You’ll use it constantly: python manage.py runserver, python manage.py migrate, python manage.py createsuperuser. Almost never modify it.
conquered/__init__.py
An empty file that tells Python “this directory is a package.” You’ll occasionally add things here (like initializing Celery), but for now leave it alone.
conquered/settings.py
The brain of your project. Database config, installed apps, middleware, template directories, static file paths, time zones — all of it lives here. We’ll come back to this file constantly throughout the series.
A few things worth pointing out right now:
DEBUG = True— fine for development, never for production. Among other things it leaks stack traces to the browser.SECRET_KEY— used for signing sessions, CSRF tokens, password resets, etc. Move this into an environment variable before you push to GitHub. (Yes, even for a hobby project.)ALLOWED_HOSTS— a list of hostnames Django will respond to. In production, set this explicitly.INSTALLED_APPS— all the Django apps active in your project. You’ll add to this list as you build features.
conquered/urls.py
The root URL configuration. Every request that comes into Django is matched against the urlpatterns list here, then dispatched to a view. You’ll typically include other apps’ URL files from this root file using include().
conquered/wsgi.py and conquered/asgi.py
The entrypoints for production servers. WSGI is the synchronous standard (Gunicorn, uWSGI). ASGI is the async-capable successor (Daphne, Uvicorn). Don’t touch these files until you’re deploying.
Step 5: Run the development server
python manage.py runserver
You’ll see something like:
Watching for file changes with StatReloader
Django version 5.0, using settings 'conquered.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Open http://127.0.0.1:8000/ in your browser. You should see Django’s “The install worked successfully!” rocket. Also notice the warning about unapplied migrations — let’s fix that next.
Step 6: Run initial migrations
Django comes with built-in apps (auth, sessions, admin) that need database tables. Apply their migrations:
python manage.py migrate
You’ll see Django create a db.sqlite3 file. We’ll swap SQLite for PostgreSQL in a later post
, but it’s a fine default for getting started.
Step 7: Create your first app
In Django, a project is the whole site, and an app is a self-contained module of features (e.g., blog, accounts, payments). Let’s create one:
python manage.py startapp blog
This generates:
blog/
├── __init__.py
├── admin.py # register models with the admin
├── apps.py # app config
├── migrations/ # database migration files (auto-generated)
│ └── __init__.py
├── models.py # data models
├── tests.py # tests for this app
└── views.py # request handlers
Then register the app in conquered/settings.py:
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"blog", # <-- add this
]
Step 8: Wire up a “Hello” view
In blog/views.py:
from django.http import HttpResponse
def hello(request):
return HttpResponse("Hello, Django Conquered!")
Create blog/urls.py:
from django.urls import path
from . import views
urlpatterns = [
path("", views.hello, name="hello"),
]
And include it in conquered/urls.py:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("blog/", include("blog.urls")),
]
Restart the dev server (it auto-reloads, but the URL change benefits from a full restart) and visit http://127.0.0.1:8000/blog/. You should see your message.
Wrapping up
You now have:
- A virtual environment isolating your dependencies.
- A Django project (
conquered) with all its config files demystified. - A Django app (
blog) with a working URL, view, and a route to it. - Migrations applied so the database is in a known state.
In the next post we’ll dive into models: how the ORM turns Python classes into database tables, what migrations actually do under the hood, and how to design models that won’t bite you a year from now.
See you in the next one!
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .