Skip to content

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>&copy; 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#

  1. Use template inheritance - Create base template
  2. Organize templates - Keep app templates in templates/app_name/
  3. Use includes - Reuse template snippets
  4. Load static once - Load {% load static %} in base template
  5. Escape by default - Django auto-escapes, use |safe carefully
  6. 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)!