Django

Django | Django From Scratch

Luv Baniya

Django | Django From Scratch
Understanding Django — Rujal Baniya

1. The Internet: Client ↔ Server

Every website on earth works the same fundamental way: a client asks for something, and a server answers. That's it. Everything else — Django, databases, templates — is detail on top of this single conversation.

The client is your browser (Chrome, Firefox, Safari). The server is just another computer — usually in a data center somewhere — running software that knows how to respond to web requests. Django is that software.

Diagram 1 — Client / Server Model
Browser CLIENT Your computer Internet TCP / IP GET /blog/ 200 OK + HTML Django SERVER Data center

When you type rujalbaniya.com in your browser, your browser looks up the IP address of that server, opens a connection, and says "Give me the home page." The server runs its code (Django in our case), builds the HTML, and sends it back. Your browser renders the HTML as a visual page. Done.

💡
Key mental model: The server never reaches out to you. It only ever responds. Every piece of HTML, CSS, or data you see in your browser was explicitly requested by your browser first.

2. How HTTP Works

The language clients and servers speak is called HTTP (HyperText Transfer Protocol). Every request has a method and a path. Every response has a status code and a body.

MethodWhat it meansExample
GETFetch a page or resourceLoad a blog post
POSTSend data to the serverSubmit a form
PUT / PATCHUpdate an existing resourceEdit a profile
DELETERemove a resourceDelete a comment
Status codeMeaning
200 OKEverything worked, here's your content
301 / 302Redirect — go here instead
404 Not FoundThat page doesn't exist
500 Server ErrorSomething broke on our end

Django's job is to look at the incoming request — its method, path, and any data it carries — and decide what HTML (or JSON, or redirect) to send back.

3. What Django Does

Django is a Python web framework. A framework is code that handles the boring, repetitive infrastructure so you can focus on your actual product. Without a framework, building a website means manually parsing HTTP requests, writing SQL queries with sanitization, building forms with CSRF protection, handling sessions... thousands of lines before you write a single line of business logic.

Django handles all of that. You write Python functions that receive requests and return responses. Django handles everything in between.

Diagram 2 — What Happens Inside Django
Request GET /blog/ Middleware CSRF, Auth… URL conf urls.py match View your logic Response HTML / JSON Browser Security Routing Business Browser

4. The MTV Pattern

Django organises code around a pattern called MTV: Model, Template, View. You may have heard of MVC (Model-View-Controller) from other frameworks — Django's version is the same idea, just with different names.

Diagram 3 — Django's MTV Architecture
Browser sends HTTP request URLconf urls.py — route matching VIEW views.py — your logic lives here MODEL models.py + DB Database TEMPLATE HTML + Django tags rendered HTML read/write

Think of it this way:

  • Model — defines your data and talks to the database
  • Template — the HTML skeleton with placeholders for dynamic data
  • View — the brain: fetches data from the Model, passes it to the Template, returns the result
The View is not what the user sees. The Template is what the user sees. The View is the logic that decides which template to show and what data to put in it.

5. URLs — The Router

The first thing Django does with every request is figure out which view to call. It does this by matching the URL path against a list of patterns defined in urls.py.

Diagram 4 — URL Routing
Request GET /blog/3/ urls.py path('', home_view) path('blog/<id>/', post_view) path('about/', about_view) ✓ match post_view views.py function id=3 passed as argument

Here's what a real urls.py looks like:

# myproject/urls.py
from django.urls import path
from myapp import views

urlpatterns = [
    path('',                     views.home,         name='home'),
    path('about/',               views.about,        name='about'),
    path('blog/',                views.blog_list,    name='blog_list'),
    path('blog/<int:post_id>/', views.blog_detail,  name='blog_detail'),
]

The angle-bracket syntax — <int:post_id> — captures a part of the URL and passes it to your view as a named Python argument. So /blog/42/ calls blog_detail(request, post_id=42).

Always name your URL patterns (the name= argument). This lets you write {% verbatim %}{% url 'blog_detail' post.id %}{% endverbatim %} in templates instead of hardcoding paths — so if you ever change a URL, you only change it in one place.

6. Views — The Logic

A view is a Python function (or class) that receives an HTTP request object and returns an HTTP response. That's the complete definition. Everything in between — database queries, business logic, template rendering — is your code.

# myapp/views.py
from django.shortcuts import render, get_object_or_404
from .models import Post

def blog_list(request):
    # 1. Fetch data from the database
    posts = Post.objects.filter(is_published=True).order_by('-published_at')

    # 2. Pass it to a template — Django renders and returns HTML
    return render(request, 'blog/list.html', {'posts': posts})


def blog_detail(request, post_id):
    # get_object_or_404 returns the post, or a 404 page if it doesn't exist
    post = get_object_or_404(Post, id=post_id, is_published=True)
    return render(request, 'blog/detail.html', {'post': post})

The render() shortcut does three things in one call: takes the request, finds your template file, passes the context dictionary into it, and returns the rendered HTML as a HttpResponse.

Function-Based vs Class-Based Views

Django offers two styles. Function-Based Views (FBVs) are plain Python functions — straightforward, easy to read, great for beginners. Class-Based Views (CBVs) are classes that inherit from Django generics, reducing boilerplate for common patterns (list pages, detail pages, forms).

# The same blog list as a Class-Based View
from django.views.generic import ListView
from .models import Post

class BlogListView(ListView):
    model = Post
    template_name = 'blog/list.html'
    context_object_name = 'posts'

    def get_queryset(self):
        return Post.objects.filter(is_published=True)
📌
Start with function-based views. They make the request-response flow explicit and easy to trace. Move to class-based views when you find yourself writing the same list/detail/form patterns repeatedly.

7. Templates — The HTML

Templates are HTML files with special Django tags that get replaced with real data before being sent to the browser. The template language is intentionally simple — it's not Python, and that's on purpose.

<!-- templates/blog/list.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Blog</title>
</head>
<body>

  <h1>All Posts</h1>

  {% verbatim %}{% for post in posts %}{% endverbatim %}
    <article>
      <h2><a href="{% verbatim %}{% url 'blog_detail' post.id %}{% endverbatim %}">{{ post.title }}</a></h2>
      <p>{{ post.excerpt }}</p>
      <time>{{ post.published_at|date:"M j, Y" }}</time>
    </article>
  {% verbatim %}{% empty %}{% endverbatim %}
    <p>No posts yet.</p>
  {% verbatim %}{% endfor %}{% endverbatim %}

</body>
</html>

Template Inheritance — The Most Important Feature

Copying the same <head>, navbar, and footer into every template would be terrible. Django solves this with template inheritance. You define a base layout with named blocks, then child templates fill in only what's different.

Diagram 5 — Template Inheritance
base.html <head> + fonts + CSS {% verbatim %}{% block content %}{% endverbatim %} <footer> <scripts> home.html {% verbatim %}{% extends 'base.html' %}{% endverbatim %} {% verbatim %}{% block content %}{% endverbatim %} Hero section here {% verbatim %}{% endblock %}{% endverbatim %} blog/list.html {% verbatim %}{% extends 'base.html' %}{% endverbatim %} {% verbatim %}{% block content %}{% endverbatim %} List of posts here {% verbatim %}{% endblock %}{% endverbatim %} blog/detail.html {% verbatim %}{% extends 'base.html' %}{% endverbatim %} {% verbatim %}{% block content %}{% endverbatim %} Single post here {% verbatim %}{% endblock %}{% endverbatim %}
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{% verbatim %}{% block title %}My Site{% endblock %}{% endverbatim %}</title>
    <link rel="stylesheet" href="{% verbatim %}{% static 'styles.css' %}{% endverbatim %}">
</head>
<body>
    <nav><a href="/">Home</a></nav>

    {% verbatim %}{% block content %}{% endverbatim %}
    <!-- child templates fill this in -->
    {% verbatim %}{% endblock %}{% endverbatim %}

    <footer>© 2025</footer>
</body>
</html>

<!-- templates/blog/detail.html -->
{% verbatim %}{% extends 'base.html' %}

{% block title %}{{ post.title }}{% endblock %}

{% block content %}{% endverbatim %}
  <h1>{{ post.title }}</h1>
  <div>{{ post.body|safe }}</div>
{% verbatim %}{% endblock %}{% endverbatim %}

Child templates only define what's different. The navbar, footer, and <head> are written once in base.html and automatically included everywhere.

8. Models — The Database

A Django model is a Python class that maps directly to a database table. Each class attribute becomes a column. You never write SQL by hand — Django generates it from your Python classes.

Diagram 6 — Python Model → Database Table
models.py — Python class Post (models.Model): title = CharField(max_length=200) body = TextField() author = ForeignKey(User) published_at = DateTimeField() is_published = BooleanField() makemigrations + migrate DB Table: myapp_post id title author_id is_pub 1 Why Django? 5 true 2 Django ORM 5 true 3 Draft post 5 false
# myapp/models.py
from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)

    def __str__(self):
        return self.name


class Post(models.Model):
    title        = models.CharField(max_length=200)
    slug         = models.SlugField(unique=True)
    body         = models.TextField()
    excerpt      = models.TextField(blank=True)
    author       = models.ForeignKey(User, on_delete=models.CASCADE)
    category     = models.ForeignKey(Category, null=True, on_delete=models.SET_NULL)
    published_at = models.DateTimeField(null=True, blank=True)
    is_published = models.BooleanField(default=False)
    created_at   = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-published_at']

    def __str__(self):
        return self.title

Common Field Types

FieldUse it for
CharField(max_length=N)Short strings — titles, names
TextField()Long text — body content, descriptions
IntegerField()Whole numbers
BooleanField()True / False
DateTimeField()Date + time
ImageField()File upload path for images
ForeignKey(Model)Many-to-one relationship
ManyToManyField(Model)Many-to-many (e.g. tags)

Querying the Database — The ORM

Django's ORM (Object-Relational Mapper) lets you query the database using Python instead of SQL. Every model has a built-in objects manager:

# All published posts
posts = Post.objects.filter(is_published=True)

# Most recent 5 posts
recent = Post.objects.filter(is_published=True).order_by('-published_at')[:5]

# Single post by slug (raises 404 if not found)
post = get_object_or_404(Post, slug='why-django')

# Create a new post
Post.objects.create(title='Hello', body='World', author=request.user)

# Update
Post.objects.filter(id=1).update(is_published=True)

# Delete
Post.objects.filter(is_published=False).delete()

Migrations — Keeping DB in Sync

Every time you change a model, you run two commands:

1
python manage.py makemigrations
Django reads your models, compares them to the last known state, and writes a migration file describing the changes (add column, drop table, etc.).
2
python manage.py migrate
Django applies any unapplied migration files to the actual database — running the SQL to add columns, create tables, etc.
⚠️
Never delete migration files from version control. They are the history of your database schema. Losing them makes it very hard to reproduce the database on a new server or roll back a change.

9. admin.py — Free Control Panel

One of Django's most powerful features ships with zero effort: the automatic admin interface. Register your models in admin.py, and Django generates a full CRUD interface — create, read, update, delete — with search, filters, and pagination included.

# myapp/admin.py
from django.contrib import admin
from .models import Post, Category

# Simple registration
admin.site.register(Category)

# Customised registration
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display   = ['title', 'author', 'category', 'is_published', 'published_at']
    list_filter    = ['is_published', 'category']
    search_fields  = ['title', 'body']
    prepopulated_fields = {'slug': ('title',)}
    date_hierarchy = 'published_at'
    ordering       = ['-published_at']

Access it at /admin/ after creating a superuser:

python manage.py createsuperuser
Diagram 7 — What the Admin Gives You Automatically
admin.py @admin.register List View + Filters Search + Pagination Create / Edit Delete + Confirm Permissions + Auth History / Audit log

10. settings.py & Other Important Files

A fresh Django project generates a handful of files. Here's what each one does:

FileWhat it does
settings.py Master config: database, installed apps, static files, secret key, allowed hosts, templates, middleware
urls.py Root URL configuration — maps paths to views (usually includes app-level urls.py files)
wsgi.py / asgi.py Entry points for production servers (gunicorn / uvicorn). You rarely touch these
manage.py CLI tool for runserver, makemigrations, migrate, createsuperuser, shell, etc.
models.py Your data structures (one per app)
views.py Your business logic functions (one per app)
admin.py Register models to appear in the admin panel (one per app)
apps.py App configuration class — you register your app in INSTALLED_APPS
forms.py Form classes (not auto-generated, you create this)
signals.py Hooks to run code when models are saved/deleted (you create this)

The most important section of settings.py to understand early:

# settings.py — the parts you'll edit most

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'myapp',          # ← your app goes here
]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [BASE_DIR / 'templates'],  # ← where your HTML lives
    ...
}]

STATIC_URL = '/static/'
MEDIA_URL  = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

11. Full Request Flow — Putting It All Together

Let's trace exactly what happens when a user visits rujalbaniya.com/blog/42/ from start to finish:

Diagram 8 — Complete Request Lifecycle
① Browser GET /blog/42/ → server IP ② Middleware SecurityMiddleware, SessionMiddleware… ③ urls.py path('blog/<id>/', blog_detail) ④ View blog_detail(request, post_id=42) calls models, prepares context ⑤ Model DB query Post obj ⑥ Template blog/detail.html + context → HTML ⑦ HttpResponse status 200, Content-Type: text/html ⑧ Browser renders User sees the blog post page
1
Browser sends GET /blog/42/
TCP connection opens, HTTP request is sent to the server's IP on port 80 or 443.
2
Middleware runs
Django runs the request through each middleware in order — CSRF check, session loading, auth, etc. If any middleware rejects the request, it short-circuits and returns a response here.
3
urls.py matches the pattern
path('blog/<int:post_id>/', blog_detail) matches, so Django calls blog_detail with post_id=42.
4
View function runs
Your Python code executes. It queries the database, runs business logic, and decides what context to pass to the template.
5
Model fetches data
The ORM translates Post.objects.get(id=42) into a SQL query, runs it against the database, and returns a Python object.
6
Template renders
Django finds blog/detail.html, fills in all the {{ }} variables and {% %} tags with the real data, and produces a complete HTML string.
7
HttpResponse sent back
The HTML string is wrapped in an HTTP response with a 200 status code and sent back through the middleware stack (which can modify it) and out over the network.
8
Browser renders
Your browser parses the HTML, requests CSS and JS files, and paints the final page. The user sees the blog post.

Quick Reference — Commands You'll Use Every Day

# Start a new project
django-admin startproject myproject

# Create an app inside the project
python manage.py startapp myapp

# Run the development server (localhost:8000)
python manage.py runserver

# Create migration files from model changes
python manage.py makemigrations

# Apply migrations to the database
python manage.py migrate

# Create a superuser for the admin panel
python manage.py createsuperuser

# Open a Python shell with Django loaded
python manage.py shell

# Collect static files for production
python manage.py collectstatic

That's the foundation. Every Django project you'll ever build lives on top of these eight concepts: the client-server model, HTTP, settings, URLs, views, templates, models, and the admin. Master how they connect, and the rest is just detail.

Next up: building a complete blog with Django — from startproject to a deployed page, including forms, authentication, and media uploads.

Related notes