Om Pandey
Django Framework

Django Framework

The Web Framework for Perfectionists with Deadlines

Master Django - Python's most powerful web framework. Build robust, scalable backend applications with clean, pragmatic design. From zero to production-ready!

Fast Development Secure by Default ORM Included Admin Panel

What You'll Learn

1

Introduction to Django

Django is a high-level Python web framework that enables rapid development of secure and maintainable websites. Built by experienced developers, Django takes care of much of the hassle of web development, so you can focus on writing your app without needing to reinvent the wheel.

💡 Why Django?

Batteries Included: Django comes with everything you need - ORM, admin panel, authentication, and more. Used by Instagram, Pinterest, Spotify, and Mozilla!

🏗️ Django MTV Architecture
👤 User Request
📍 URLs
👁️ Views
📄 Templates
📤 Response
MTV Pattern:
  • Model: Data layer - defines your database structure
  • Template: Presentation layer - HTML files with Django template language
  • View: Business logic layer - handles requests and returns responses
2

Installation & Setup

1

Install Python (if not installed)

Make sure you have Python 3.8+ installed. Check with python --version

2

Create Virtual Environment

Always use virtual environments to isolate project dependencies

Terminal
$ # Create virtual environment $ python -m venv venv $ # Activate on Windows $ venv\Scripts\activate $ # Activate on Mac/Linux $ source venv/bin/activate $ # Install Django $ pip install django Collecting django Installing collected packages: django Successfully installed django-5.0 $ # Verify installation $ django-admin --version 5.0

✅ Django Installed!

You're ready to create your first Django project!

3

Create Project & Apps

Create New Project

Terminal
$ # Create project $ django-admin startproject myproject $ # Navigate into project $ cd myproject $ # Run development server $ python manage.py runserver Starting development server at http://127.0.0.1:8000/

🎉 Open http://127.0.0.1:8000/ in your browser - You'll see the Django welcome page!

📁 myproject/ ├── 📁 myproject/ │ ├── __init__.py │ ├── settings.py # Project configuration │ ├── urls.py # URL routing │ ├── asgi.py # ASGI config │ └── wsgi.py # WSGI config ├── manage.py # Command-line utility └── db.sqlite3 # SQLite database (created after migrate)

Create an App

A Django project can contain multiple apps. Each app handles a specific feature (e.g., blog, users, products).

Terminal
$ # Create a new app called 'blog' $ python manage.py startapp blog
📁 blog/ ├── 📁 migrations/ # Database migrations ├── __init__.py ├── admin.py # Admin panel config ├── apps.py # App configuration ├── models.py # Database models ├── tests.py # Unit tests └── views.py # View functions

⚠️ Don't Forget!

After creating an app, you must add it to INSTALLED_APPS in settings.py

myproject/settings.py
Python
# Add your app to INSTALLED_APPS

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    # Your apps
    'blog',  # Add this line!
]
4

URLs & Routing

URLs define the routes in your application. When a user visits a URL, Django uses the URL configuration to determine which view to call.

Project URLs (Main Router)

myproject/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    # Admin panel
    path('admin/', admin.site.urls),
    
    # Include blog app urls
    path('blog/', include('blog.urls')),
    
    # Home page
    path('', include('blog.urls')),
]

App URLs

Create a new file urls.py inside your app folder:

blog/urls.py
from django.urls import path
from . import views

urlpatterns = [
    # Home page - /
    path('', views.home, name='home'),
    
    # About page - /about/
    path('about/', views.about, name='about'),
    
    # Dynamic URL with parameter - /post/1/
    path('post/<int:id>/', views.post_detail, name='post_detail'),
    
    # String parameter - /category/tech/
    path('category/<str:name>/', views.category, name='category'),
]

💡 URL Parameters

<int:id> - Integer parameter
<str:name> - String parameter
<slug:slug> - Slug (letters, numbers, hyphens, underscores)

5

Views & Functions

Views handle the business logic. They receive HTTP requests and return HTTP responses. Django supports both function-based views (FBV) and class-based views (CBV).

Function-Based Views

blog/views.py
from django.shortcuts import render
from django.http import HttpResponse

# Simple response
def home(request):
    return HttpResponse("<h1>Welcome to My Blog!</h1>")

# Render template with context
def about(request):
    context = {
        'name': 'Om Pandey',
        'skills': ['Python', 'Django', 'React'],
    }
    return render(request, 'blog/about.html', context)

# View with URL parameter
def post_detail(request, id):
    return HttpResponse(f"<h1>Post ID: {id}</h1>")

# View with string parameter
def category(request, name):
    return HttpResponse(f"<h1>Category: {name}</h1>")
Browser Output - /about/
Name: Om Pandey Skills: Python, Django, React

Class-Based Views

blog/views.py
from django.views import View
from django.views.generic import ListView, DetailView, CreateView
from .models import Post

# Basic Class-Based View
class HomeView(View):
    def get(self, request):
        return render(request, 'blog/home.html')
    
    def post(self, request):
        # Handle POST request
        pass

# Generic ListView - List all posts
class PostListView(ListView):
    model = Post
    template_name = 'blog/post_list.html'
    context_object_name = 'posts'
    ordering = ['-created_at']
    paginate_by = 10

# Generic DetailView - Single post
class PostDetailView(DetailView):
    model = Post
    template_name = 'blog/post_detail.html'
blog/urls.py (for CBV)
from django.urls import path
from .views import HomeView, PostListView, PostDetailView

urlpatterns = [
    path('', HomeView.as_view(), name='home'),
    path('posts/', PostListView.as_view(), name='post_list'),
    path('post/<int:pk>/', PostDetailView.as_view(), name='post_detail'),
]
6

Templates & HTML

Templates are HTML files with Django Template Language (DTL) for dynamic content.

Template Structure

📁 blog/ └── 📁 templates/ └── 📁 blog/ ├── base.html # Base template ├── home.html # Home page ├── post_list.html # List of posts └── post_detail.html # Single post

Base Template (Layout)

blog/templates/blog/base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}My Blog{% endblock %}</title>
    
    <!-- Load static files -->
    {% load static %}
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
    <nav>
        <a href="{% url 'home' %}">Home</a>
        <a href="{% url 'about' %}">About</a>
    </nav>

    <main>
        {% block content %}
        <!-- Page content goes here -->
        {% endblock %}
    </main>

    <footer>
        <p>© 2025 My Blog</p>
    </footer>
</body>
</html>

Child Template

blog/templates/blog/home.html
{% extends 'blog/base.html' %}

{% block title %}Home - My Blog{% endblock %}

{% block content %}
<h1>Welcome, {{ user.username }}!</h1>

<!-- Display variable -->
<p>{{ message }}</p>

<!-- If statement -->
{% if posts %}
    <h2>Latest Posts</h2>
    
    <!-- For loop -->
    {% for post in posts %}
        <article>
            <h3>{{ post.title }}</h3>
            <p>{{ post.content|truncatewords:30 }}</p>
            <small>Posted on {{ post.created_at|date:"M d, Y" }}</small>
            <a href="{% url 'post_detail' post.id %}">Read More</a>
        </article>
    {% empty %}
        <p>No posts available.</p>
    {% endfor %}
{% else %}
    <p>No posts yet.</p>
{% endif %}
{% endblock %}

💡 Template Tags & Filters

{{ variable }} - Output variable
{% tag %} - Template tag (if, for, url, etc.)
{{ text|filter }} - Apply filter (truncate, date, upper, etc.)

7

Models & Database

Models define your database structure. Django's ORM (Object-Relational Mapping) lets you interact with the database using Python code instead of SQL.

Creating Models

blog/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)
    
    class Meta:
        verbose_name_plural = 'Categories'
    
    def __str__(self):
        return self.name


class Post(models.Model):
    # Choices for status
    STATUS_CHOICES = [
        ('draft', 'Draft'),
        ('published', 'Published'),
    ]
    
    # Fields
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    content = models.TextField()
    excerpt = models.TextField(blank=True)
    image = models.ImageField(upload_to='posts/', blank=True)
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
    
    # Relationships
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
    
    # Timestamps
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        ordering = ['-created_at']
    
    def __str__(self):
        return self.title


class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
    author = models.CharField(max_length=100)
    email = models.EmailField()
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return f'Comment by {self.author} on {self.post}'

Migrations

After creating or modifying models, you need to create and apply migrations.

Terminal
$ # Create migration files $ python manage.py makemigrations Migrations for 'blog': blog/migrations/0001_initial.py - Create model Category - Create model Post - Create model Comment $ # Apply migrations to database $ python manage.py migrate Applying blog.0001_initial... OK

Working with Models (ORM)

Django Shell / views.py
from blog.models import Post, Category

# CREATE - Add new record
post = Post.objects.create(
    title="My First Post",
    slug="my-first-post",
    content="This is my first blog post!",
    author=user,
    status='published'
)

# READ - Get all posts
all_posts = Post.objects.all()

# READ - Get single post
post = Post.objects.get(id=1)
post = Post.objects.get(slug='my-first-post')

# FILTER - Get specific posts
published_posts = Post.objects.filter(status='published')
recent_posts = Post.objects.filter(created_at__year=2025)

# ORDER BY
latest_posts = Post.objects.order_by('-created_at')[:5]

# UPDATE
post = Post.objects.get(id=1)
post.title = "Updated Title"
post.save()

# UPDATE - Bulk update
Post.objects.filter(status='draft').update(status='published')

# DELETE
post = Post.objects.get(id=1)
post.delete()

# DELETE - Bulk delete
Post.objects.filter(status='draft').delete()

# RELATED OBJECTS
post = Post.objects.get(id=1)
comments = post.comments.all()  # Get all comments for this post

# COUNT
total_posts = Post.objects.count()

# EXISTS
has_posts = Post.objects.filter(author=user).exists()
8

Settings Configuration

Important Settings

myproject/settings.py
import os
from pathlib import Path

# Build paths
BASE_DIR = Path(__file__).resolve().parent.parent

# SECURITY - Keep secret in production!
SECRET_KEY = 'your-secret-key-here'

# Debug mode (False in production)
DEBUG = True

# Allowed hosts
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'yourdomain.com']

# Installed Apps
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    # Third-party apps
    'rest_framework',
    'corsheaders',
    
    # Your apps
    'blog',
    'users',
]

# Database - Default SQLite
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# Templates
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

# Static files (CSS, JS, Images)
STATIC_URL = 'static/'
STATICFILES_DIRS = [BASE_DIR / 'static']
STATIC_ROOT = BASE_DIR / 'staticfiles'

# Media files (User uploads)
MEDIA_URL = 'media/'
MEDIA_ROOT = BASE_DIR / 'media'

# Time zone
TIME_ZONE = 'Asia/Kathmandu'
USE_TZ = True

# Language
LANGUAGE_CODE = 'en-us'

# Login/Logout redirects
LOGIN_REDIRECT_URL = 'home'
LOGOUT_REDIRECT_URL = 'home'
9

PostgreSQL Connection

SQLite is great for development, but PostgreSQL is recommended for production.

1

Install PostgreSQL Driver

Install psycopg2 to connect Django with PostgreSQL

Terminal
$ pip install psycopg2-binary
2

Configure Database Settings

Update your settings.py with PostgreSQL configuration

myproject/settings.py
# PostgreSQL Database Configuration

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydatabase',           # Database name
        'USER': 'myuser',               # Database user
        'PASSWORD': 'mypassword',       # Database password
        'HOST': 'localhost',            # Database host
        'PORT': '5432',                 # Default PostgreSQL port
    }
}

# Better approach - Use environment variables
import os

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ.get('DB_NAME', 'mydatabase'),
        'USER': os.environ.get('DB_USER', 'myuser'),
        'PASSWORD': os.environ.get('DB_PASSWORD', ''),
        'HOST': os.environ.get('DB_HOST', 'localhost'),
        'PORT': os.environ.get('DB_PORT', '5432'),
    }
}
3

Apply Migrations

Run migrations to create tables in PostgreSQL

Terminal
$ python manage.py migrate Operations to perform: Apply all migrations... Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK ...

✅ PostgreSQL Connected!

Your Django project is now using PostgreSQL database!

10

Forms & User Input

Django forms handle user input validation and HTML rendering.

Creating Forms

blog/forms.py
from django import forms
from .models import Post, Comment

# Simple Form
class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField()
    message = forms.CharField(widget=forms.Textarea)
    
    def clean_email(self):
        email = self.cleaned_data.get('email')
        if not email.endswith('@gmail.com'):
            raise forms.ValidationError('Please use Gmail address')
        return email


# ModelForm - Auto-generates from model
class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content', 'category', 'status']
        widgets = {
            'title': forms.TextInput(attrs={'class': 'form-control'}),
            'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 5}),
        }


# Comment Form
class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['author', 'email', 'body']

Handling Forms in Views

blog/views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import PostForm, ContactForm

# Create post with form
def create_post(request):
    if request.method == 'POST':
        form = PostForm(request.POST, request.FILES)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.save()
            messages.success(request, 'Post created successfully!')
            return redirect('post_detail', pk=post.pk)
    else:
        form = PostForm()
    
    return render(request, 'blog/create_post.html', {'form': form})


# Contact form handling
def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            # Access cleaned data
            name = form.cleaned_data['name']
            email = form.cleaned_data['email']
            message = form.cleaned_data['message']
            
            # Send email or save to database
            # send_mail(...)
            
            messages.success(request, 'Message sent!')
            return redirect('contact')
    else:
        form = ContactForm()
    
    return render(request, 'blog/contact.html', {'form': form})

Form in Template

blog/templates/blog/create_post.html
{% extends 'blog/base.html' %}

{% block content %}
<h1>Create New Post</h1>

<!-- Display messages -->
{% if messages %}
    {% for message in messages %}
        <div class="alert alert-{{ message.tags }}">{{ message }}</div>
    {% endfor %}
{% endif %}

<form method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    
    <!-- Render all fields -->
    {{ form.as_p }}
    
    <!-- Or render individually -->
    <div class="form-group">
        <label for="{{ form.title.id_for_label }}">Title</label>
        {{ form.title }}
        {% if form.title.errors %}
            <span class="error">{{ form.title.errors }}</span>
        {% endif %}
    </div>
    
    <button type="submit">Create Post</button>
</form>
{% endblock %}

⚠️ CSRF Token Required!

Always include {% csrf_token %} in forms to prevent Cross-Site Request Forgery attacks.

11

Admin Panel

Django's built-in admin panel is a powerful tool for managing your data.

Terminal
$ # Create superuser $ python manage.py createsuperuser Username: admin Email: [email protected] Password: ******** Superuser created successfully.

Register Models in Admin

blog/admin.py
from django.contrib import admin
from .models import Post, Category, Comment

# Simple registration
admin.site.register(Category)

# Custom admin with features
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'category', 'status', 'created_at']
    list_filter = ['status', 'category', 'created_at']
    search_fields = ['title', 'content']
    prepopulated_fields = {'slug': ('title',)}
    date_hierarchy = 'created_at'
    ordering = ['-created_at']
    list_per_page = 20
    
    # Inline editing
    list_editable = ['status']


@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
    list_display = ['author', 'post', 'created_at']
    list_filter = ['created_at']
    search_fields = ['author', 'body']

Access admin at: http://127.0.0.1:8000/admin/

12

Advanced Topics

Authentication

blog/views.py
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin

# Protect view with decorator
@login_required
def dashboard(request):
    return render(request, 'blog/dashboard.html')

# Protect class-based view with mixin
class CreatePostView(LoginRequiredMixin, CreateView):
    model = Post
    form_class = PostForm
    template_name = 'blog/create_post.html'
    
    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)

REST API with Django REST Framework

Terminal
$ pip install djangorestframework
blog/serializers.py
from rest_framework import serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ['id', 'title', 'content', 'author', 'created_at']
blog/api_views.py
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from .models import Post
from .serializers import PostSerializer

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    
    def perform_create(self, serializer):
        serializer.save(author=self.request.user)
API Response - GET /api/posts/
{ "count": 2, "results": [ { "id": 1, "title": "My First Post", "content": "Hello World!", "author": 1, "created_at": "2025-01-25T10:00:00Z" }, ... ] }

Deployment Checklist

Production settings.py
# Production settings

# Security
DEBUG = False
SECRET_KEY = os.environ.get('SECRET_KEY')
ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']

# HTTPS settings
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000

# Static files
STATIC_ROOT = BASE_DIR / 'staticfiles'

# Email configuration
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
Terminal - Deployment Commands
$ # Collect static files $ python manage.py collectstatic $ # Check deployment $ python manage.py check --deploy $ # Generate requirements.txt $ pip freeze > requirements.txt

🎉 Congratulations!

You've learned Django from zero to hero! You're now ready to build powerful backend applications. Keep practicing and building projects!