Complete Django Tutorial

Master all Django essentials with short, practical examples!

๐Ÿš€ Welcome to Django!

Django helps you build websites super fast! It's like having a complete toolkit for web development.


from django.http import HttpResponse

def hello(request):
    return HttpResponse("Hello Django World! ๐ŸŒ")
                                    
Fast
Development
Secure
By Default
Scalable
Architecture

Getting Started

Installation & Setup : Installing Django via pip, creating projects with django-admin startproject

Install and Create Project

# Install Django
pip install django

# Create project
django-admin startproject mysite
cd mysite

# Run server
python manage.py runserver

Django Project Structure

Understanding Django's MVC pattern with settings.py, urls.py, and apps

Understanding the Structure

mysite/
    manage.py          # Command-line utility
    mysite/
        __init__.py
        settings.py    # Project settings
        urls.py        # URL routing
        wsgi.py        # Web server gateway
        asgi.py        # Async server gateway

Creating Your First App

Create and Register App

# Create app
python manage.py startapp blog

# mysite/settings.py - Add to INSTALLED_APPS
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',  # Add your app here
]

Creating Views

Defining views to handle HTTP requests and return responses

Function-Based Views

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

def home(request):
    return HttpResponse("Welcome to my blog!")

def about(request):
    return HttpResponse("About page")

def contact(request):
    context = {'title': 'Contact Us'}
    return render(request, 'blog/contact.html', context)
Class-Based Views

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

class PostListView(ListView):
    model = Post
    template_name = 'blog/home.html'
    context_object_name = 'posts'
    paginate_by = 5

class PostDetailView(DetailView):
    model = Post
    template_name = 'blog/post_detail.html'

URL Routing

Mapping URLs to views and creating dynamic routes

App URLs

# blog/urls.py (create this file)
from django.urls import path
from . import views

app_name = 'blog'
urlpatterns = [
    path('', views.home, name='home'),
    path('about/', views.about, name='about'),
    path('post//', views.PostDetailView.as_view(), name='post-detail'),
    path('posts/', views.PostListView.as_view(), name='post-list'),
]
Main Project URLs

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls')),
    path('blog/', include('blog.urls')),
]

Database Models

Defining database schema with Django ORM, including relationships (ForeignKey)

Creating Models

# blog/models.py
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse

class Category(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField(blank=True)
    
    def __str__(self):
        return self.name

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
    created_date = models.DateTimeField(auto_now_add=True)
    updated_date = models.DateTimeField(auto_now=True)
    published = models.BooleanField(default=False)
    
    def __str__(self):
        return self.title
    
    def get_absolute_url(self):
        return reverse('blog:post-detail', kwargs={'pk': self.pk})
Database Operations

# Create and apply migrations
python manage.py makemigrations
python manage.py migrate

# Create superuser
python manage.py createsuperuser

Templates

Base Template


<!DOCTYPE html>
<html>
<head>
    <title>{% raw %}{% block title %}My Blog{% endblock %}{% endraw %}</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand" href="{% raw %}{% url 'blog:home' %}{% endraw %}">My Blog</a>
        </div>
    </nav>
    
    <div class="container mt-4">
        {% raw %}{% block content %}{% endblock %}{% endraw %}
    </div>
<script src="script.js"></script>
</body>
</html>
Post List Template


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

{% block content %}

Latest Posts

{% for post in posts %}
{{ post.title }}

{{ post.content|truncatewords:20 }}

By {{ post.author }} on {{ post.created_date|date:"F d, Y" }} Read More
{% empty %}

No posts yet.

{% endfor %} {% endblock %}

Django Forms

Creating Forms

# blog/forms.py
from django import forms
from .models import Post, Category

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content', 'category', 'published']
        widgets = {
            'title': forms.TextInput(attrs={'class': 'form-control'}),
            'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
            'category': forms.Select(attrs={'class': 'form-control'}),
        }

class ContactForm(forms.Form):
    name = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(widget=forms.EmailInput(attrs={'class': 'form-control'}))
    message = forms.CharField(widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 5}))
Form Views

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

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

def contact_view(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            # Process form data
            messages.success(request, 'Message sent successfully!')
            return redirect('blog:contact')
    else:
        form = ContactForm()
    return render(request, 'blog/contact.html', {'form': form})

Admin Interface

Register Models

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

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'category', 'published', 'created_date']
    list_filter = ['published', 'created_date', 'category']
    search_fields = ['title', 'content']
    list_editable = ['published']
    date_hierarchy = 'created_date'

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ['name', 'description']
    search_fields = ['name']

User Authentication

Login/Logout Views

# blog/views.py
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required

def login_view(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(request, username=username, password=password)
        if user is not None:
            login(request, user)
            return redirect('blog:home')
        else:
            messages.error(request, 'Invalid credentials')
    return render(request, 'blog/login.html')

def logout_view(request):
    logout(request)
    return redirect('blog:home')

@login_required
def profile_view(request):
    return render(request, 'blog/profile.html')
User Registration

# blog/forms.py
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class SignUpForm(UserCreationForm):
    email = forms.EmailField(required=True)
    
    class Meta:
        model = User
        fields = ('username', 'email', 'password1', 'password2')

# blog/views.py
def signup_view(request):
    if request.method == 'POST':
        form = SignUpForm(request.POST)
        if form.is_valid():
            user = form.save()
            username = form.cleaned_data.get('username')
            messages.success(request, f'Account created for {username}!')
            return redirect('blog:login')
    else:
        form = SignUpForm()
    return render(request, 'blog/signup.html', {'form': form})

Static Files (CSS, JS, Images)

Static Files Configuration

# mysite/settings.py
import os

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    BASE_DIR / "static",
]
STATIC_ROOT = BASE_DIR / "staticfiles"

MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / "media"
Using Static Files in Templates


{% raw %}{% load static %}{% endraw %}
<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" type="text/css" href="{% raw %}{% static 'blog/style.css' %}{% endraw %}">
    <script src="{% raw %}{% static 'blog/script.js' %}{% endraw %}"></script>
</head>
<body>
    <img src="{% raw %}{% static 'blog/images/logo.png' %}{% endraw %}" alt="Logo">
    <!-- Content -->
<script src="script.js"></script>
</body>
</html>

Database Queries

Basic Queries

# In views.py or Django shell
from .models import Post, Category

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

# Filter posts
published_posts = Post.objects.filter(published=True)
recent_posts = Post.objects.filter(created_date__gte='2024-01-01')

# Get single object
post = Post.objects.get(id=1)
# Or safely: post = Post.objects.get_or_404(id=1)

# Order posts
latest_posts = Post.objects.order_by('-created_date')[:5]

# Count posts
post_count = Post.objects.filter(published=True).count()
Advanced Queries

# Complex filtering
from django.db.models import Q

# OR condition
posts = Post.objects.filter(Q(title__icontains='django') | Q(content__icontains='python'))

# Exclude
posts = Post.objects.exclude(published=False)

# Related objects
posts_with_author = Post.objects.select_related('author', 'category')

# Aggregation
from django.db.models import Count, Avg
category_stats = Category.objects.annotate(post_count=Count('post'))

Middleware

Custom Middleware

# blog/middleware.py
class SimpleMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # Code before view
        print(f"Request to: {request.path}")
        
        response = self.get_response(request)
        
        # Code after view
        print(f"Response status: {response.status_code}")
        return response

# Add to settings.py MIDDLEWARE
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'blog.middleware.SimpleMiddleware',  # Add here
    # ... other middleware
]

Testing

Writing Tests

# blog/tests.py
from django.test import TestCase, Client
from django.contrib.auth.models import User
from django.urls import reverse
from .models import Post, Category

class PostModelTest(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(username='testuser', password='testpass')
        self.category = Category.objects.create(name='Test Category')
        
    def test_post_creation(self):
        post = Post.objects.create(
            title='Test Post',
            content='Test content',
            author=self.user,
            category=self.category
        )
        self.assertEqual(post.title, 'Test Post')
        self.assertEqual(str(post), 'Test Post')

class PostViewTest(TestCase):
    def test_home_view(self):
        response = self.client.get(reverse('blog:home'))
        self.assertEqual(response.status_code, 200)
Run Tests

# Run all tests
python manage.py test

# Run specific app tests
python manage.py test blog

# Run with coverage
pip install coverage
coverage run --source='.' manage.py test
coverage report

Deployment Basics

Production Settings

# mysite/settings.py (production)
import os

DEBUG = False
ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']

# Security settings
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

# Database (example for PostgreSQL)
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ.get('DB_NAME'),
        'USER': os.environ.get('DB_USER'),
        'PASSWORD': os.environ.get('DB_PASSWORD'),
        'HOST': os.environ.get('DB_HOST'),
        'PORT': os.environ.get('DB_PORT'),
    }
}
Requirements File

# Create requirements.txt
pip freeze > requirements.txt

# Install from requirements
pip install -r requirements.txt

# Collect static files for production
python manage.py collectstatic

๐ŸŽฏ Real-World Example

Complete Blog View

# blog/views.py - Complete example
from django.shortcuts import render, get_object_or_404
from django.core.paginator import Paginator
from django.db.models import Q
from .models import Post, Category

def blog_home(request):
    # Get all published posts
    posts = Post.objects.filter(published=True).select_related('author', 'category')
    
    # Search functionality
    search_query = request.GET.get('search')
    if search_query:
        posts = posts.filter(
            Q(title__icontains=search_query) | 
            Q(content__icontains=search_query)
        )
    
    # Category filter
    category_id = request.GET.get('category')
    if category_id:
        posts = posts.filter(category_id=category_id)
    
    # Pagination
    paginator = Paginator(posts, 5)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    
    # Context
    context = {
        'page_obj': page_obj,
        'categories': Category.objects.all(),
        'search_query': search_query,
        'selected_category': category_id,
    }
    return render(request, 'blog/home.html', context)

Best Practices

๐Ÿ—๏ธ Follow MVT Pattern

Keep Models, Views, and Templates separate

๐Ÿ”’ Security First

Use Django's built-in security features

๐Ÿ—„๏ธ Optimize Queries

Use select_related() and prefetch_related()

๐Ÿงช Write Tests

Test your models, views, and forms

๐Ÿง  Quick Quiz

What does MVT stand for in Django?

Which command creates database tables?