Table of Contents 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 1. What is Django? Django is a high level Python web framework that encourages rapid developme…
Table of Contents
- What is Django?
- Setting Up Your Environment
- Your First Project
- The Django Architecture: MTV
- URLs and Views
- Models and the Database
- The Django Admin
- Templates
- Forms
- Static Files and Media
- Authentication and Users
- Building a Small Project: A Notes App
- Deployment Basics
- Where to Go Next
1. What is Django?
Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design. It was created in 2003 by developers at a newspaper in Lawrence, Kansas, who needed to build and ship web applications on newsroom deadlines — which is why Django is often described as "the web framework for perfectionists with deadlines."
Django takes care of much of the hassle of web development, so you can focus on writing your application without needing to reinvent the wheel. It is free, open source, and has a mature ecosystem with excellent documentation.
Why choose Django?
- Batteries included — Django ships with an ORM, an admin interface, a templating engine, authentication, form handling, and security protections out of the box.
- Fast development — Its conventions and code generation tools let you move from idea to working prototype quickly.
- Secure by default — Django helps developers avoid common security mistakes like SQL injection, cross-site scripting (XSS), cross-site request forgery (CSRF), and clickjacking.
- Scalable — Companies like Instagram, Spotify, Pinterest, and Disqus have used Django at massive scale.
- Versatile — You can build almost anything: content management systems, social networks, scientific computing platforms, e-commerce sites, and REST APIs (with Django REST Framework).
Who is this book for?
This book assumes basic familiarity with Python (variables, functions, classes, and modules) but no prior web development experience. By the end, you should be able to build a simple, functioning, database-backed web application from scratch.
2. Setting Up Your Environment
2.1 Prerequisites
You will need:
- Python 3.10 or newer installed on your machine
- A code editor (VS Code, PyCharm, or similar)
- Basic command-line comfort
Check your Python version:
python3 --version
2.2 Creating a Virtual Environment
Virtual environments keep your project's dependencies isolated from other Python projects on your system. This is considered best practice for every Django project.
python3 -m venv venv
Activate it:
# macOS / Linux
source venv/bin/activate
# Windows
venv\Scripts\activate
You'll know it worked because your terminal prompt will now show (venv) at the start of the line.
2.3 Installing Django
With your virtual environment active:
pip install django
Verify the installation:
python3 -m django --version
3. Your First Project
3.1 Creating a Project
A Django project is the overall container for your application — it holds configuration and can contain multiple apps (self-contained modules of functionality).
django-admin startproject mysite
cd mysite
This generates the following structure:
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
manage.py — a command-line utility for interacting with your project (running the server, creating apps, managing the database, and more).
settings.py — all project configuration: installed apps, database settings, middleware, templates, static files, and more.
urls.py — the top-level URL routing table.
wsgi.py / asgi.py — entry points used by web servers to run your application in production.
3.2 Running the Development Server
python3 manage.py runserver
Visit http://127.0.0.1:8000/ in your browser. You should see Django's welcome page — congratulations, your project is running.
3.3 Creating an App
Projects are made up of apps. An app is a self-contained bundle of models, views, and templates that does one thing well (e.g. a blog, a polls app, a store).
python3 manage.py startapp notes
This creates:
notes/
migrations/
__init__.py
admin.py
apps.py
models.py
tests.py
views.py
Register the app in mysite/settings.py:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'notes', # our new app
]
4. The Django Architecture: MTV
Django follows a pattern it calls MTV — Model, Template, View — which is a variation of the classic MVC pattern.
| Layer |
Responsibility |
| Model |
Defines the structure of your data and talks to the database |
| Template |
Defines how data is presented (the HTML) |
| View |
Contains the logic that connects models to templates, processing requests and returning responses |
In Django's terminology, the framework itself acts as the "Controller," automatically routing requests to the right view based on the URL — which is why some describe Django as following MTV rather than MVC.
The typical request/response cycle looks like this:
- A user's browser sends a request to a URL.
- Django's URL dispatcher matches that URL to a view function or class.
- The view may talk to one or more models to fetch or save data.
- The view passes data to a template, which renders it into HTML.
- Django sends the rendered HTML back as an HTTP response.
5. URLs and Views
5.1 A Simple View
Views are Python functions (or classes) that take a web request and return a web response. Add this to notes/views.py:
from django.http import HttpResponse
def home(request):
return HttpResponse("Welcome to my Notes app!")
5.2 Wiring Up URLs
Create notes/urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'),
]
Then include it in the project's main mysite/urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('notes.urls')),
]
Restart the server and visit http://127.0.0.1:8000/ — you should see your custom message.
5.3 URL Parameters
You can capture parts of the URL as variables:
# notes/urls.py
urlpatterns = [
path('', views.home, name='home'),
path('note/<int:note_id>/', views.note_detail, name='note_detail'),
]
# notes/views.py
def note_detail(request, note_id):
return HttpResponse(f"You requested note #{note_id}")
5.4 Class-Based Views (a preview)
Django also supports class-based views, which package common patterns (listing objects, showing details, handling forms) into reusable classes:
from django.views.generic import ListView
from .models import Note
class NoteListView(ListView):
model = Note
template_name = 'notes/note_list.html'
We'll return to these once models are introduced.
6. Models and the Database
6.1 Defining a Model
Models describe the shape of your data as Python classes. Django's ORM (Object-Relational Mapper) translates these classes into database tables automatically — you rarely need to write raw SQL.
# notes/models.py
from django.db import models
class Note(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
is_pinned = models.BooleanField(default=False)
def __str__(self):
return self.title
Common field types include CharField, TextField, IntegerField, BooleanField, DateField, DateTimeField, ForeignKey, and ManyToManyField.
6.2 Migrations
Django tracks changes to your models through migrations — files that describe how to alter the database schema.
python3 manage.py makemigrations
python3 manage.py migrate
makemigrations looks at your models and generates migration files describing the changes.
migrate applies those changes to the actual database.
By default, Django uses SQLite for development — a lightweight, file-based database that requires no setup. For production, you'll typically switch to PostgreSQL or MySQL by changing the DATABASES setting in settings.py.
6.3 Querying the Database (the ORM Shell)
Django provides an interactive shell for experimenting with your models:
python3 manage.py shell
>>> from notes.models import Note
>>> Note.objects.create(title="Shopping list", content="Milk, eggs, bread")
>>> Note.objects.all()
<QuerySet [<Note: Shopping list>]>
>>> Note.objects.filter(is_pinned=True)
>>> Note.objects.get(id=1)
Some common ORM methods:
| Method |
Purpose |
.all() |
Get every record |
.filter(**kwargs) |
Get records matching conditions |
.exclude(**kwargs) |
Get records that don't match conditions |
.get(**kwargs) |
Get a single record (raises an error if none or multiple match) |
.order_by('field') |
Sort results |
.delete() |
Remove records |
6.4 Relationships
Models can relate to each other. For example, giving each note an author:
from django.contrib.auth.models import User
class Note(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
ForeignKey creates a many-to-one relationship — many notes can belong to one user. on_delete=models.CASCADE means that if the user is deleted, their notes are deleted too.
7. The Django Admin
One of Django's most celebrated features is its automatically generated admin interface — a ready-made dashboard for managing your data.
7.1 Creating a Superuser
python3 manage.py createsuperuser
Follow the prompts to set a username, email, and password.
7.2 Registering a Model
# notes/admin.py
from django.contrib import admin
from .models import Note
admin.site.register(Note)
Visit http://127.0.0.1:8000/admin/ and log in. You'll see a full CRUD (Create, Read, Update, Delete) interface for the Note model — generated entirely for free.
7.3 Customizing the Admin
@admin.register(Note)
class NoteAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'created_at', 'is_pinned')
list_filter = ('is_pinned', 'created_at')
search_fields = ('title', 'content')
8. Templates
Templates define how your data becomes HTML. Django's template language lets you insert variables, loop over data, and apply logic — all with a restricted, designer-friendly syntax (it deliberately avoids full Python execution in templates for security and separation of concerns).
8.1 Setting Up the Templates Folder
By convention, create notes/templates/notes/note_list.html. The nested folder (notes/notes/) avoids naming collisions between apps.
8.2 A Basic Template
<!-- notes/templates/notes/note_list.html -->
<!DOCTYPE html>
<html>
<head>
<title>My Notes</title>
</head>
<body>
<h1>My Notes</h1>
<ul>
{% for note in notes %}
<li>
<strong>{{ note.title }}</strong> — {{ note.created_at|date:"F j, Y" }}
</li>
{% empty %}
<li>No notes yet.</li>
{% endfor %}
</ul>
</body>
</html>
Key syntax:
{{ variable }} — output a value
{% tag %} — template logic like loops and conditionals
|filter — transform a value (e.g. {{ note.title|upper }})
8.3 Rendering a Template from a View
from django.shortcuts import render
from .models import Note
def note_list(request):
notes = Note.objects.all().order_by('-created_at')
return render(request, 'notes/note_list.html', {'notes': notes})
8.4 Template Inheritance
Rather than repeating HTML boilerplate on every page, Django lets templates extend a shared base:
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head><title>{% block title %}My Site{% endblock %}</title></head>
<body>
<nav>My Notes App</nav>
{% block content %}{% endblock %}
</body>
</html>
<!-- notes/templates/notes/note_list.html -->
{% extends "base.html" %}
{% block title %}All Notes{% endblock %}
{% block content %}
<ul>
{% for note in notes %}
<li>{{ note.title }}</li>
{% endfor %}
</ul>
{% endblock %}
9. Forms
Django's forms library handles rendering HTML forms, validating input, and protecting against malicious submissions.
9.1 A Model Form
# notes/forms.py
from django import forms
from .models import Note
class NoteForm(forms.ModelForm):
class Meta:
model = Note
fields = ['title', 'content', 'is_pinned']
9.2 Using It in a View
def note_create(request):
if request.method == 'POST':
form = NoteForm(request.POST)
if form.is_valid():
note = form.save(commit=False)
note.author = request.user
note.save()
return redirect('note_list')
else:
form = NoteForm()
return render(request, 'notes/note_form.html', {'form': form})
9.3 Rendering the Form and CSRF Protection
Every form that submits data with POST must include Django's CSRF token to protect against cross-site request forgery attacks:
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Save</button>
</form>
10. Static Files and Media
Static files are your CSS, JavaScript, and images that don't change per request. Media files are user-uploaded content.
10.1 Static Files
In settings.py:
STATIC_URL = '/static/'
STATICFILES_DIRS = [BASE_DIR / 'static']
In a template:
{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
10.2 Media Files
For user uploads (e.g. profile pictures), configure:
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
And a model field:
avatar = models.ImageField(upload_to='avatars/', blank=True)
11. Authentication and Users
Django ships with a complete authentication system: user accounts, groups, permissions, and session-based login.
11.1 Login and Logout Views
# mysite/urls.py
from django.contrib.auth import views as auth_views
urlpatterns = [
path('login/', auth_views.LoginView.as_view(), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
]
Django expects a template at registration/login.html by default.
11.2 Restricting Access
from django.contrib.auth.decorators import login_required
@login_required
def note_create(request):
...
Unauthenticated users are automatically redirected to the login page.
12. Building a Small Project: A Notes App
Let's tie everything together into a minimal but complete app.
notes/models.py
from django.db import models
from django.contrib.auth.models import User
class Note(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
notes/views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from .models import Note
from .forms import NoteForm
@login_required
def note_list(request):
notes = Note.objects.filter(author=request.user).order_by('-created_at')
return render(request, 'notes/note_list.html', {'notes': notes})
@login_required
def note_create(request):
if request.method == 'POST':
form = NoteForm(request.POST)
if form.is_valid():
note = form.save(commit=False)
note.author = request.user
note.save()
return redirect('note_list')
else:
form = NoteForm()
return render(request, 'notes/note_form.html', {'form': form})
@login_required
def note_delete(request, pk):
note = get_object_or_404(Note, pk=pk, author=request.user)
note.delete()
return redirect('note_list')
notes/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.note_list, name='note_list'),
path('new/', views.note_create, name='note_create'),
path('<int:pk>/delete/', views.note_delete, name='note_delete'),
]
This gives you a working app where logged-in users can create, view, and delete their own private notes — the skeleton of countless real-world applications.
13. Deployment Basics
Getting a Django app from your laptop onto the internet involves a few key steps:
- Set
DEBUG = False in production settings — never leave debug mode on publicly, as it exposes sensitive internal information.
- Set
ALLOWED_HOSTS to your domain name(s).
- Use a production database like PostgreSQL instead of SQLite.
- Serve static files properly — typically with
whitenoise or a CDN, since Django's development server isn't meant for serving static assets in production.
- Use a real web server — Gunicorn or uWSGI behind Nginx, rather than
manage.py runserver.
- Set a strong, secret
SECRET_KEY and load it from an environment variable rather than hardcoding it.
- Use HTTPS in production, and enable Django's security middleware settings (
SECURE_SSL_REDIRECT, SESSION_COOKIE_SECURE, etc.).
Common hosting choices include Railway, Render, Heroku-style platforms, DigitalOcean, or a self-managed VPS.
14. Where to Go Next
You now have a working knowledge of Django's core pieces: projects, apps, models, views, templates, forms, the admin, and authentication. From here:
- Django REST Framework (DRF) — build APIs on top of your models for mobile apps or JavaScript frontends.
- Class-based generic views —
ListView, DetailView, CreateView, UpdateView, DeleteView reduce boilerplate significantly once you're comfortable with the function-based versions.
- Testing — Django includes a built-in testing framework (
django.test.TestCase) for writing unit and integration tests.
- Signals — hook custom logic into model events like
post_save or pre_delete.
- Django's official tutorial ("Writing your first Django app") — an excellent next step that builds a polls application in more depth than this book.
- The Django documentation — consistently ranked among the best-written docs of any major framework; well worth reading directly once you have the basics.
Django rewards patience: its conventions can feel like "magic" at first, but they exist to eliminate repetitive work once you understand them. Build small projects, break things, read the error messages carefully (Django's are unusually informative), and the pieces will click into place.
Happy building.