Part 1, Project setup and the MVT pattern
Install
Use a fresh virtualenv. Always.
python -m venv .venvsource .venv/bin/activatepip install --upgrade pippip install djangoVerify:
django-admin --version # 5.2.xStart a project
Django has two organizational units: the project (the top-level config) and apps (self-contained features). One project, many apps.
django-admin startproject mysite .# The `.` matters, it puts files in the current dir rather than making an extra mysite/ wrapper.
python manage.py migrate # creates the initial SQLite DBpython manage.py runserver # http://127.0.0.1:8000/You should now see the Django green rocket.
What just got generated
.├── manage.py # CLI entry point├── mysite/│ ├── __init__.py│ ├── settings.py # all config lives here│ ├── urls.py # root URL dispatcher│ ├── asgi.py # ASGI server entrypoint (async, WebSockets, HTTP/2)│ └── wsgi.py # WSGI server entrypoint (traditional)└── db.sqlite3 # created on first migrateKey knobs in settings.py:
DEBUG = True, local only; leaks stack traces otherwise.ALLOWED_HOSTS = [], must include your domain in production.INSTALLED_APPS, the apps Django knows about.MIDDLEWARE, ordered list of request/response processors.DATABASES, SQLite by default; swap to Postgres for anything real.SECRET_KEY, signing key; never commit a production one.
Create your first app
python manage.py startapp blogAdd "blog" to INSTALLED_APPS in settings.py.
The MVT pattern
Django calls its architecture MVT, Model, View, Template. It’s basically MVC with different names:
| Django term | What it is | Equivalent elsewhere |
|---|---|---|
| Model | Python class mapping to a DB table | MVC Model |
| View | Function or class taking a request, returning a response | MVC Controller (note: not View!) |
| Template | HTML file with Django’s template language | MVC View |
Confusion alert: Django’s “view” is MVC’s “controller.” The thing called “view” in other frameworks is Django’s “template.”
The request flow
HTTP request │ ▼WSGI/ASGI server (gunicorn, uvicorn) │ ▼MIDDLEWARE (security, sessions, CSRF, auth, ...) │ ▼URL dispatcher (mysite/urls.py → app/urls.py) │ ▼VIEW (function or class-based) │ ↕ reads/writes via MODELS (ORM) ▼TEMPLATE (rendered with context) │ ▼HTTP responseYour first URL → view → template
blog/views.py
from django.http import HttpResponsefrom django.shortcuts import render
def hello(request): return HttpResponse("Hello, Django.")
def homepage(request): return render(request, "blog/home.html", {"name": "World"})blog/urls.py (new file)
from django.urls import pathfrom . import views
app_name = "blog" # namespace for reverse() lookups
urlpatterns = [ path("hello/", views.hello, name="hello"), path("", views.homepage, name="home"),]mysite/urls.py
from django.contrib import adminfrom django.urls import include, path
urlpatterns = [ path("admin/", admin.site.urls), path("", include("blog.urls")),]blog/templates/blog/home.html (the nested blog/ is intentional, Django searches all template dirs, so namespacing prevents collisions)
<!DOCTYPE html><html><head><title>Home</title></head><body> <h1>Hello, {{ name }}!</h1></body></html>Restart runserver (it usually auto-reloads) and hit http://127.0.0.1:8000/, you should see “Hello, World!”
Gotchas at this stage
STATICFILES_DIRSvsSTATIC_ROOT,_DIRSis where Django looks for static files in development;_ROOTis wherecollectstaticputs them in production. You’ll want both eventually.- Template namespace, always nest templates under an app-named folder (
blog/templates/blog/home.html). Without this, two apps withhome.htmlcollide silently. app_namein urls.py, enables{% url "blog:home" %}in templates. Add it from day one; it’s annoying to retrofit later.- Migrations before server,
python manage.py migratebeforerunserverthe first time, or you’ll see “no such table” errors. - Python 3.12+, Django 5.2 supports Python 3.10–3.12; pick one and pin it in your project.
What’s next
Part 2 introduces models, migrations, and the ORM, the reason most people actually use Django.