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 .