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! ๐")
Getting Started
Installation & Setup : Installing Django via pip, creating projects with
# 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
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 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
# 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)
# 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
# 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'),
]
# 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)
# 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})
# Create and apply migrations
python manage.py makemigrations
python manage.py migrate
# Create superuser
python manage.py createsuperuser
Templates
<!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>
{% 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
# 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}))
# 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
# 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
# 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')
# 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)
# 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"
{% 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
# 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()
# 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
# 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
# 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 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
# 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'),
}
}
# 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
# 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