Learn Django views, templates, context data, template inheritance, and URL patterns for web development.
Learn Django views, templates, context data, template inheritance, and URL patterns for web development.
Learn how to create function-based views in Django to handle web requests and render templates.
Content by: Manali Trivedi
Python Django Developer
Views are Python functions that take a web request and return a web response. They handle the logic of your web application and determine what data gets sent to the template.
# views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, Http404
from django.views.decorators.http import require_http_methods
from .models import Post, Category
def home(request):
"""Home page view"""
posts = Post.objects.filter(published_date__isnull=False).order_by('-published_date')[:5]
context = {
'posts': posts,
'title': 'Welcome to My Blog'
}
return render(request, 'blog/home.html', context)
def post_list(request):
"""List all published posts"""
posts = Post.objects.filter(published_date__isnull=False).order_by('-published_date')
return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, pk):
"""Show a single post"""
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})
def category_posts(request, category_slug):
"""Show posts by category"""
category = get_object_or_404(Category, slug=category_slug)
posts = Post.objects.filter(category=category, published_date__isnull=False)
return render(request, 'blog/category_posts.html', {
'category': category,
'posts': posts
})
# HTTP method decorators
from django.views.decorators.http import require_http_methods
@require_http_methods(["GET", "POST"])
def contact(request):
if request.method == 'POST':
# Handle form submission
name = request.POST.get('name')
email = request.POST.get('email')
message = request.POST.get('message')
# Process the form data
return HttpResponse('Thank you for your message!')
else:
# Show the contact form
return render(request, 'blog/contact.html')
# Custom HTTP responses
def api_data(request):
"""Return JSON data"""
from django.http import JsonResponse
data = {
'posts': list(Post.objects.values('title', 'created_date')[:10])
}
return JsonResponse(data)
def download_file(request, filename):
"""Serve a file for download"""
from django.http import FileResponse
import os
file_path = os.path.join('media', 'documents', filename)
if os.path.exists(file_path):
return FileResponse(open(file_path, 'rb'))
else:
raise Http404("File not found")Test your understanding of this topic:
Explore Django's class-based views for reusable, object-oriented web request handling.
Content by: Manali Trivedi
Python Django Developer
Class-based views provide an object-oriented way to organize code and reuse functionality. They're more powerful and flexible than function-based views.
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from .models import Post, Category
from .forms import PostForm
# List View
class PostListView(ListView):
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'
paginate_by = 10
ordering = ['-published_date']
def get_queryset(self):
"""Filter to only published posts"""
return Post.objects.filter(published_date__isnull=False)
def get_context_data(self, **kwargs):
"""Add extra context data"""
context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all()
return context
# Detail View
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
def get_object(self):
"""Get the post or return 404"""
return get_object_or_404(Post, pk=self.kwargs['pk'])
# Create View
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
form_class = PostForm
template_name = 'blog/post_form.html'
success_url = reverse_lazy('post_list')
def form_valid(self, form):
"""Set the author before saving"""
form.instance.author = self.request.user
return super().form_valid(form)
# Update View
class PostUpdateView(LoginRequiredMixin, UpdateView):
model = Post
form_class = PostForm
template_name = 'blog/post_form.html'
def get_success_url(self):
"""Redirect to the updated post"""
return reverse_lazy('post_detail', kwargs={'pk': self.object.pk})
# Delete View
class PostDeleteView(LoginRequiredMixin, DeleteView):
model = Post
template_name = 'blog/post_confirm_delete.html'
success_url = reverse_lazy('post_list')
# Custom CBV with mixins
from django.contrib.auth.mixins import UserPassesTestMixin
class UserPostListView(LoginRequiredMixin, ListView):
model = Post
template_name = 'blog/user_posts.html'
context_object_name = 'posts'
def get_queryset(self):
"""Show only user's posts"""
return Post.objects.filter(author=self.request.user)
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
form_class = PostForm
template_name = 'blog/post_form.html'
def test_func(self):
"""Check if user is the author of the post"""
post = self.get_object()
return self.request.user == post.authorTest your understanding of this topic:
Learn Django's template system for creating dynamic HTML pages with template inheritance.
Content by: Manali Trivedi
Python Django Developer
Django templates are HTML files with special template syntax that allows you to dynamically generate content. Templates separate presentation logic from business logic.
<!-- base.html (Base template) -->
<!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>
<link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
<header>
<nav>
<a href="{% url 'home' %}">Home</a>
<a href="{% url 'post_list' %}">Blog</a>
<a href="{% url 'about' %}">About</a>
<a href="{% url 'contact' %}">Contact</a>
</nav>
</header>
<main>
{% block content %}
{% endblock %}
</main>
<footer>
<p>© 2024 My Blog. All rights reserved.</p>
</footer>
</body>
</html>
<!-- post_list.html (Extends base template) -->
{% extends 'base.html' %}
{% load static %}
{% block title %}Blog Posts{% endblock %}
{% block content %}
<div class="container">
<h1>Blog Posts</h1>
{% if posts %}
<div class="posts-grid">
{% for post in posts %}
<article class="post-card">
<h2><a href="{% url 'post_detail' post.pk %}">{{ post.title }}</a></h2>
<p class="meta">
By {{ post.author.username }} |
{{ post.published_date | date:"F j, Y" }}
</p>
<p>{{ post.content | truncatewords:30 }}</p>
{% if post.category %}
<span class="category">{{ post.category.name }}</span>
{% endif %}
</article>
{% empty %}
<p>No posts available.</p>
{% endfor %}
</div>
<!-- Pagination -->
{% if is_paginated %}
<div class="pagination">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}">« Previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">Next »</a>
{% endif %}
</div>
{% endif %}
{% else %}
<p>No posts found.</p>
{% endif %}
</div>
{% endblock %}
<!-- post_detail.html -->
{% extends 'base.html' %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
<article class="post-detail">
<header>
<h1>{{ post.title }}</h1>
<div class="meta">
<p>By {{ post.author.username }} | {{ post.published_date | date:"F j, Y" }}</p>
{% if post.category %}
<p>Category: <a href="{% url 'category_posts' post.category.slug %}">{{ post.category.name }}</a></p>
{% endif %}
</div>
</header>
<div class="content">
{{ post.content | linebreaks }}
</div>
{% if user == post.author %}
<div class="actions">
<a href="{% url 'post_update' post.pk %}" class="btn btn-edit">Edit</a>
<a href="{% url 'post_delete' post.pk %}" class="btn btn-delete">Delete</a>
</div>
{% endif %}
</article>
{% endblock %}Test your understanding of this topic:
Master Django's template tags and filters for dynamic content generation and conditional logic.
Content by: Manali Trivedi
Python Django Developer
Django provides powerful template tags and filters for dynamic content generation, conditional logic, and data manipulation.
<!-- Template Tags -->
{% load static %}
{% load humanize %}
<!-- Variables and filters -->
<h1>{{ post.title|title }}</h1>
<p>{{ post.content|truncatewords:50 }}</p>
<p>Published: {{ post.published_date|date:"F j, Y" }}</p>
<p>Views: {{ post.views|intcomma }}</p>
<!-- Conditional statements -->
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}!</p>
{% if user.is_staff %}
<a href="{% url 'admin:index' %}">Admin Panel</a>
{% endif %}
{% else %}
<p>Please <a href="{% url 'login' %}">login</a> to continue.</p>
{% endif %}
<!-- Loops -->
{% for post in posts %}
<article>
<h2>{{ forloop.counter }}. {{ post.title }}</h2>
{% if forloop.first %}
<span class="featured">Featured Post</span>
{% endif %}
{% if forloop.last %}
<span class="latest">Latest Post</span>
{% endif %}
</article>
{% empty %}
<p>No posts available.</p>
{% endfor %}
<!-- URL tags -->
<a href="{% url 'post_detail' post.pk %}">Read More</a>
<!-- Include and extends -->
{% extends 'base.html' %}
{% include 'sidebar.html' %}
<!-- Custom template tags -->
{% load blog_extras %}
<!-- Custom filter -->
<p>{{ post.content|markdown }}</p>
<!-- Template inheritance with blocks -->
{% block sidebar %}
<aside class="sidebar">
<h3>Categories</h3>
<ul>
{% for category in categories %}
<li>
<a href="{% url 'category_posts' category.slug %}">
{{ category.name }} ({{ category.post_set.count }})
</a>
</li>
{% endfor %}
</ul>
</aside>
{% endblock %}
<!-- Custom template tags (create in templatetags/blog_extras.py) -->
from django import template
from django.utils.safestring import mark_safe
import markdown
register = template.Library()
@register.filter(name='markdown')
def markdown_format(text):
return mark_safe(markdown.markdown(text))
@register.simple_tag
def get_recent_posts(count=5):
return Post.objects.filter(published_date__isnull=False).order_by('-published_date')[:count]
@register.inclusion_tag('blog/post_list.html')
def show_recent_posts(count=5):
recent_posts = Post.objects.filter(published_date__isnull=False).order_by('-published_date')[:count]
return {'posts': recent_posts}
<!-- Using custom tags in templates -->
{% load blog_extras %}
<!-- Simple tag -->
{% get_recent_posts 3 as recent_posts %}
{% for post in recent_posts %}
<h3>{{ post.title }}</h3>
{% endfor %}
<!-- Inclusion tag -->
{% show_recent_posts 5 %}Test your understanding of this topic:
Learn advanced view patterns including mixins, decorators, and custom view behaviors for complex applications.
Content by: Manali Trivedi
Python Django Developer
Django provides powerful mixins and decorators that allow you to add functionality to views without duplicating code. These patterns help create reusable, maintainable view logic.
# Custom mixins for views
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.shortcuts import redirect
from django.contrib import messages
class AuthorRequiredMixin:
"""Mixin to ensure only the author can access the view"""
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
if obj.author != request.user:
raise PermissionDenied("You don't have permission to access this resource.")
return super().dispatch(request, *args, **kwargs)
class StaffRequiredMixin:
"""Mixin to ensure only staff members can access the view"""
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
messages.error(request, "Access denied. Staff privileges required.")
return redirect('home')
return super().dispatch(request, *args, **kwargs)
class CacheControlMixin:
"""Mixin to add cache control headers"""
def dispatch(self, request, *args, **kwargs):
response = super().dispatch(request, *args, **kwargs)
response['Cache-Control'] = 'public, max-age=300' # 5 minutes
return response
# Custom decorators
def author_required(view_func):
"""Decorator to ensure only the author can access the view"""
def wrapper(request, *args, **kwargs):
from .models import Post
post = get_object_or_404(Post, pk=kwargs['pk'])
if post.author != request.user:
messages.error(request, "You don't have permission to edit this post.")
return redirect('post_detail', pk=post.pk)
return view_func(request, *args, **kwargs)
return wrapper
def rate_limit(limit=100, period=3600):
"""Decorator to implement rate limiting"""
def decorator(view_func):
def wrapper(request, *args, **kwargs):
from django.core.cache import cache
from django.http import HttpResponseTooManyRequests
# Create a unique key for this user/IP
key = f"rate_limit:{request.user.id if request.user.is_authenticated else request.META.get('REMOTE_ADDR')}"
# Check current count
current_count = cache.get(key, 0)
if current_count >= limit:
return HttpResponseTooManyRequests("Rate limit exceeded. Please try again later.")
# Increment count
cache.set(key, current_count + 1, period)
return view_func(request, *args, **kwargs)
return wrapper
return decorator
# Usage examples
class PostEditView(AuthorRequiredMixin, LoginRequiredMixin, UpdateView):
model = Post
form_class = PostForm
template_name = 'blog/post_form.html'
@login_required
@author_required
@rate_limit(limit=50, period=3600)
def edit_post(request, pk):
# This view is rate-limited and author-restricted
pass# View Factory Pattern for Dynamic Views
from django.views.generic import ListView, DetailView
from django.db import models
class ViewFactory:
"""Factory class to create views dynamically"""
@staticmethod
def create_list_view(model, template_name=None, context_object_name=None, **kwargs):
"""Create a ListView for any model"""
if template_name is None:
template_name = f'{model._meta.app_label}/{model._meta.model_name}_list.html'
if context_object_name is None:
context_object_name = f'{model._meta.model_name}_list'
class DynamicListView(ListView):
model = model
template_name = template_name
context_object_name = context_object_name
def get_queryset(self):
"""Filter queryset based on request parameters"""
queryset = super().get_queryset()
# Apply filters from query parameters
for key, value in self.request.GET.items():
if hasattr(model, key) and value:
if isinstance(getattr(model, key), models.CharField):
queryset = queryset.filter(**{f'{key}__icontains': value})
else:
queryset = queryset.filter(**{key: value})
return queryset
def get_context_data(self, **kwargs):
"""Add dynamic context data"""
context = super().get_context_data(**kwargs)
# Add filter form context
context['filters'] = self.request.GET
context['model_name'] = model._meta.verbose_name_plural
return context
# Apply additional kwargs
for key, value in kwargs.items():
setattr(DynamicListView, key, value)
return DynamicListView
@staticmethod
def create_detail_view(model, template_name=None, context_object_name=None, **kwargs):
"""Create a DetailView for any model"""
if template_name is None:
template_name = f'{model._meta.app_label}/{model._meta.model_name}_detail.html'
if context_object_name is None:
context_object_name = model._meta.model_name
class DynamicDetailView(DetailView):
model = model
template_name = template_name
context_object_name = context_object_name
def get_context_data(self, **kwargs):
"""Add dynamic context data"""
context = super().get_context_data(**kwargs)
# Add related objects if they exist
obj = self.get_object()
# Look for related fields
for field in model._meta.get_fields():
if field.is_relation and not field.auto_created:
try:
if field.many_to_many:
related_objects = getattr(obj, field.name).all()
else:
related_objects = getattr(obj, field.name)
context[f'{field.name}_list'] = related_objects
except:
pass
return context
# Apply additional kwargs
for key, value in kwargs.items():
setattr(DynamicDetailView, key, value)
return DynamicDetailView
# Usage example
from .models import Post, Category, Tag
# Create views dynamically
PostListView = ViewFactory.create_list_view(
Post,
paginate_by=10,
ordering=['-created_date']
)
CategoryListView = ViewFactory.create_list_view(
Category,
template_name='blog/category_list.html'
)
TagDetailView = ViewFactory.create_detail_view(
Tag,
template_name='blog/tag_detail.html'
)
# Register in urls.py
# urlpatterns = [
# path('posts/', PostListView.as_view(), name='post_list'),
# path('categories/', CategoryListView.as_view(), name='category_list'),
# path('tag/<slug:slug>/', TagDetailView.as_view(), name='tag_detail'),
# ]Test your understanding of this topic:
Continue your learning journey and master the next set of concepts.
Continue to Module 5