Views, URLs & Templates#
Complete guide to handling requests, routing URLs, and rendering templates in Django.
🎯 Views#
Function-Based Views (FBV)#
# myapp/views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, JsonResponse
from .models import Post
def post_list(request):
"""Display list of posts"""
posts = Post.objects.all()
return render(request, 'myapp/post_list.html', {'posts': posts})
def post_detail(request, pk):
"""Display single post"""
post = get_object_or_404(Post, pk=pk)
return render(request, 'myapp/post_detail.html', {'post': post})
def api_posts(request):
"""Return JSON response"""
posts = Post.objects.all()
data = [{'id': p.id, 'title': p.title} for p in posts]
return JsonResponse({'posts': data})
Class-Based Views (CBV)#
# myapp/views.py
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Post
class PostListView(ListView):
model = Post
template_name = 'myapp/post_list.html'
context_object_name = 'posts'
paginate_by = 10
class PostDetailView(DetailView):
model = Post
template_name = 'myapp/post_detail.html'
class PostCreateView(CreateView):
model = Post
fields = ['title', 'content']
template_name = 'myapp/post_form.html'
success_url = reverse_lazy('post_list')
class PostUpdateView(UpdateView):
model = Post
fields = ['title', 'content']
template_name = 'myapp/post_form.html'
success_url = reverse_lazy('post_list')
class PostDeleteView(DeleteView):
model = Post
template_name = 'myapp/post_confirm_delete.html'
success_url = reverse_lazy('post_list')
🔗 URLs Configuration#
Root URLs#
# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('myapp.urls')), # Include app URLs
]
App URLs#
# myapp/urls.py
from django.urls import path
from . import views
app_name = 'myapp' # Namespace for URL names
urlpatterns = [
# Function-based views
path('', views.post_list, name='post_list'),
path('post/<int:pk>/', views.post_detail, name='post_detail'),
# Class-based views
path('posts/', views.PostListView.as_view(), name='post_list'),
path('post/<int:pk>/', views.PostDetailView.as_view(), name='post_detail'),
path('post/create/', views.PostCreateView.as_view(), name='post_create'),
path('post/<int:pk>/update/', views.PostUpdateView.as_view(), name='post_update'),
path('post/<int:pk>/delete/', views.PostDeleteView.as_view(), name='post_delete'),
]
URL Patterns#
# Path converters
path('post/<int:pk>/', views.detail), # Integer
path('post/<str:slug>/', views.detail), # String
path('post/<slug:slug>/', views.detail), # Slug (letters, numbers, hyphens, underscores)
path('post/<uuid:id>/', views.detail), # UUID
path('post/<path:path>/', views.detail), # Path (includes slashes)
# Multiple parameters
path('post/<int:year>/<int:month>/<slug:slug>/', views.detail),
URL Names and Reverse#
# In templates
{% url 'myapp:post_detail' pk=post.id %}
{% url 'myapp:post_list' %}
# In views
from django.urls import reverse
url = reverse('myapp:post_detail', kwargs={'pk': 1})
url = reverse('myapp:post_list')
# Redirect
from django.shortcuts import redirect
return redirect('myapp:post_detail', pk=1)
📄 Templates#
Template Structure#
myapp/
└── templates/
└── myapp/
├── base.html
├── post_list.html
└── post_detail.html
Base Template#
<!-- myapp/templates/myapp/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 Site{% endblock %}</title>
{% load static %}
<link rel="stylesheet" href="{% static 'myapp/css/style.css' %}">
</head>
<body>
<nav>
<a href="{% url 'myapp:post_list' %}">Home</a>
</nav>
<main>
{% block content %}
{% endblock %}
</main>
<footer>
<p>© 2024 My Site</p>
</footer>
<script src="{% static 'myapp/js/main.js' %}"></script>
</body>
</html>
Template Inheritance#
<!-- myapp/templates/myapp/post_list.html -->
{% extends 'myapp/base.html' %}
{% block title %}Posts - {{ block.super }}{% endblock %}
{% block content %}
<h1>All Posts</h1>
<ul>
{% for post in posts %}
<li>
<a href="{% url 'myapp:post_detail' pk=post.id %}">
{{ post.title }}
</a>
</li>
{% empty %}
<li>No posts available.</li>
{% endfor %}
</ul>
{% endblock %}
Template Tags & Filters#
Variables#
{{ post.title }} <!-- Display variable -->
{{ post.title|upper }} <!-- Apply filter -->
{{ post.title|default:"No title" }} <!-- Default value -->
Conditionals#
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}!</p>
{% else %}
<p>Please log in.</p>
{% endif %}
{% if post.status == 'published' %}
<span class="published">Published</span>
{% elif post.status == 'draft' %}
<span class="draft">Draft</span>
{% else %}
<span class="archived">Archived</span>
{% endif %}
Loops#
{% for post in posts %}
<div class="post">
<h2>{{ post.title }}</h2>
<p>{{ post.content|truncatewords:30 }}</p>
</div>
{% if forloop.first %}
<p>First post!</p>
{% endif %}
{% if forloop.last %}
<p>Last post!</p>
{% endif %}
{% if not forloop.last %}
<hr>
{% endif %}
{% empty %}
<p>No posts found.</p>
{% endfor %}
Common Filters#
{{ post.title|upper }} <!-- Uppercase -->
{{ post.title|lower }} <!-- Lowercase -->
{{ post.title|title }} <!-- Title Case -->
{{ post.content|truncatewords:30 }} <!-- Truncate words -->
{{ post.content|truncatechars:100 }} <!-- Truncate characters -->
{{ post.date|date:"Y-m-d" }} <!-- Format date -->
{{ post.date|timesince }} <!-- Time since -->
{{ post.price|floatformat:2 }} <!-- Format float -->
{{ post.content|linebreaks }} <!-- Convert line breaks -->
{{ post.content|safe }} <!-- Mark as safe HTML -->
{{ post.content|striptags }} <!-- Remove HTML tags -->
{{ post.tags|join:", " }} <!-- Join list -->
{{ post.slug|slugify }} <!-- Create slug -->
URL Tag#
{% url 'myapp:post_detail' pk=post.id %}
{% url 'myapp:post_list' %}
Static Files#
{% load static %}
<img src="{% static 'myapp/images/logo.png' %}" alt="Logo">
<link rel="stylesheet" href="{% static 'myapp/css/style.css' %}">
<script src="{% static 'myapp/js/main.js' %}"></script>
CSRF Token (Forms)#
<form method="post">
{% csrf_token %}
<!-- Form fields -->
<button type="submit">Submit</button>
</form>
Comments#
{# This is a comment #}
{% comment %}
Multi-line comment
Won't appear in HTML
{% endcomment %}
📝 Simple Forms#
Basic Form Handling#
# myapp/views.py
from django.shortcuts import render, redirect
from django.contrib import messages
def contact_view(request):
if request.method == 'POST':
name = request.POST.get('name')
email = request.POST.get('email')
message = request.POST.get('message')
# Process form data
# Send email, save to database, etc.
messages.success(request, 'Message sent successfully!')
return redirect('myapp:contact')
return render(request, 'myapp/contact.html')
<!-- myapp/templates/myapp/contact.html -->
{% extends 'myapp/base.html' %}
{% block content %}
<h1>Contact Us</h1>
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
<form method="post">
{% csrf_token %}
<div>
<label for="name">Name:</label>
<input type="text" id="name" name="name" required>
</div>
<div>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
</div>
<div>
<label for="message">Message:</label>
<textarea id="message" name="message" required></textarea>
</div>
<button type="submit">Send</button>
</form>
{% endblock %}
Using Django Forms (See Forms for details)#
# myapp/forms.py
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
# myapp/views.py
from .forms import ContactForm
def contact_view(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# Process valid form
name = form.cleaned_data['name']
email = form.cleaned_data['email']
message = form.cleaned_data['message']
messages.success(request, 'Message sent!')
return redirect('myapp:contact')
else:
form = ContactForm()
return render(request, 'myapp/contact.html', {'form': form})
<!-- Render form in template -->
<form method="post">
{% csrf_token %}
{{ form.as_p }} <!-- Render as paragraphs -->
<button type="submit">Send</button>
</form>
<!-- Or render fields individually -->
<form method="post">
{% csrf_token %}
<div>
{{ form.name.label_tag }}
{{ form.name }}
{{ form.name.errors }}
</div>
<div>
{{ form.email.label_tag }}
{{ form.email }}
{{ form.email.errors }}
</div>
<div>
{{ form.message.label_tag }}
{{ form.message }}
{{ form.message.errors }}
</div>
<button type="submit">Send</button>
</form>
🎨 Template Best Practices#
- ✅ Use template inheritance - Create base template
- ✅ Organize templates - Keep app templates in
templates/app_name/ - ✅ Use includes - Reuse template snippets
- ✅ Load static once - Load
{% load static %}in base template - ✅ Escape by default - Django auto-escapes, use
|safecarefully - ✅ Use template tags - Create custom tags for complex logic
✅ Next Steps#
- Learn about Models to create database schema
- Learn about Forms for advanced form handling
- Learn about ORM Queries to query data efficiently
Pro Tip: Use {% extends %} and {% block %} to create reusable templates. Keep templates DRY (Don't Repeat Yourself)!