Django Views & URL Routing
Build routing patterns. Differentiate class-based views (CBVs) from function-based views (FBVs), URL patterns, namespace redirects, and context processing.
Downloaded from PrepIQ (https://prepiq.online)
Q1. What is the difference between function-based views (FBVs) and class-based views (CBVs) in Django?
| Aspect | Function-based views | Class-based views |
|---|---|---|
| Definition | A plain Python function | A Python class extending View or a generic view |
| Code reuse | Harder - duplication across similar views | Easier - inheritance and mixins |
| Readability | Straightforward for simple logic | Can feel 'magic' due to inherited behavior |
| HTTP method handling | Manual if/else on request.method | Separate methods per HTTP verb (get, post, etc.) |
# Function-based view (FBV)
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
def book_detail(request, pk):
book = get_object_or_404(Book, pk=pk)
if request.method == 'POST':
# handle form submission
pass
return render(request, 'book_detail.html', {'book': book})
# Class-based view (CBV) - equivalent using View
from django.views import View
class BookDetailView(View):
def get(self, request, pk):
book = get_object_or_404(Book, pk=pk)
return render(request, 'book_detail.html', {'book': book})
def post(self, request, pk):
# handle form submission - separate method, no if/else needed
pass
# urls.py - registering each type
from django.urls import path
from . import views
urlpatterns = [
path('books/<int:pk>/', views.book_detail, name='book-detail-fbv'),
path('books-cbv/<int:pk>/', views.BookDetailView.as_view(), name='book-detail-cbv'),
]
# Generic CBV - even less code for common patterns
from django.views.generic import DetailView
class BookDetailGeneric(DetailView):
model = Book
template_name = 'book_detail.html'
# Automatically handles GET, fetches the object, and renders the template
Q2. How does Django's URL routing work with path() and re_path()?
| Function | Pattern syntax | Use case |
|---|---|---|
| path() | Simple converters like <int:pk> | Most common URLs - clean, readable |
| re_path() | Full regular expressions | Complex patterns not expressible with simple converters |
# urls.py using path() - preferred for most cases
from django.urls import path
from . import views
urlpatterns = [
path('books/', views.book_list, name='book-list'),
path('books/<int:pk>/', views.book_detail, name='book-detail'),
path('books/<slug:slug>/', views.book_by_slug, name='book-by-slug'),
path('archive/<int:year>/<int:month>/', views.archive, name='archive'),
]
# Built-in path converters:
# str - matches any non-empty string, excluding '/' (default if omitted)
# int - matches positive integers
# slug - matches letters, numbers, hyphens, underscores
# uuid - matches a formatted UUID
# path - matches any string, INCLUDING '/'
# re_path() - for patterns path() converters can't express
from django.urls import re_path
urlpatterns += [
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
re_path(r'^books/(?P<isbn>\d{3}-\d{10})/$', views.book_by_isbn),
]
# Accessing captured URL parameters in the view
def book_detail(request, pk): # 'pk' matches the <int:pk> converter name
book = get_object_or_404(Book, pk=pk)
return render(request, 'book_detail.html', {'book': book})
# Custom path converters for reusable patterns
class FourDigitYearConverter:
regex = '[0-9]{4}'
def to_python(self, value):
return int(value)
def to_url(self, value):
return '%04d' % value
from django.urls import register_converter
register_converter(FourDigitYearConverter, 'yyyy')
urlpatterns += [path('archive/<yyyy:year>/', views.year_archive)]
Q3. What are Django's generic class-based views like ListView, DetailView, and CreateView?
Generic CBVs implement common view patterns (listing, detail display, create/update/delete forms) with minimal code, following Django's DRY philosophy.
| Generic view | Purpose |
|---|---|
| ListView | Display a list of objects |
| DetailView | Display a single object's details |
| CreateView | Display and process a form to create an object |
| UpdateView | Display and process a form to edit an object |
| DeleteView | Display a confirmation and delete an object |
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Book
class BookListView(ListView):
model = Book
template_name = 'book_list.html' # defaults to book_list.html if omitted
context_object_name = 'books' # defaults to 'object_list' if omitted
paginate_by = 10
def get_queryset(self):
return Book.objects.filter(available=True).order_by('title')
class BookDetailView(DetailView):
model = Book
template_name = 'book_detail.html'
class BookCreateView(CreateView):
model = Book
fields = ['title', 'author', 'price']
template_name = 'book_form.html'
success_url = reverse_lazy('book-list') # redirect after successful creation
class BookUpdateView(UpdateView):
model = Book
fields = ['title', 'price']
template_name = 'book_form.html'
class BookDeleteView(DeleteView):
model = Book
template_name = 'book_confirm_delete.html'
success_url = reverse_lazy('book-list')
# urls.py
from django.urls import path
urlpatterns = [
path('books/', BookListView.as_view(), name='book-list'),
path('books/<int:pk>/', BookDetailView.as_view(), name='book-detail'),
path('books/new/', BookCreateView.as_view(), name='book-create'),
path('books/<int:pk>/edit/', BookUpdateView.as_view(), name='book-update'),
path('books/<int:pk>/delete/', BookDeleteView.as_view(), name='book-delete'),
]
# Overriding behavior with hooks like form_valid()
class BookCreateView(CreateView):
model = Book
fields = ['title', 'price']
def form_valid(self, form):
form.instance.created_by = self.request.user # set extra field before saving
return super().form_valid(form)
Q4. What is the difference between render(), redirect(), and HttpResponse in Django views?
| Function/class | Purpose | Status code |
|---|---|---|
| HttpResponse() | Raw response with arbitrary content | 200 by default |
| render() | Renders a template with context, returns HttpResponse | 200 by default |
| redirect() | Sends the browser to a different URL | 302 (or 301 for permanent=True) |
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.urls import reverse
# HttpResponse - raw response, manual content
def simple_view(request):
return HttpResponse('Hello, World!')
def json_view(request):
import json
data = {'status': 'ok'}
return HttpResponse(json.dumps(data), content_type='application/json')
# render() - combines a template + context + HttpResponse in one call
def book_list(request):
books = Book.objects.all()
return render(request, 'book_list.html', {'books': books})
# Equivalent to:
# from django.template import loader
# template = loader.get_template('book_list.html')
# return HttpResponse(template.render({'books': books}, request))
# redirect() - sends a 302 response with a Location header
def create_book(request):
if request.method == 'POST':
book = Book.objects.create(title=request.POST['title'])
return redirect('book-detail', pk=book.pk) # redirect to a named URL
# or: return redirect('/books/5/') # direct path
# or: return redirect(book) # uses book.get_absolute_url()
return render(request, 'book_form.html')
# Permanent redirect (301) - for URLs that have moved permanently
def old_url_view(request):
return redirect('new-url-name', permanent=True)
# JsonResponse - specialized HttpResponse for JSON APIs
from django.http import JsonResponse
def api_view(request):
return JsonResponse({'title': 'Django Basics', 'price': 25.99})
Q5. What are Django mixins and how do you use them with class-based views?
Mixins are reusable classes that add specific behavior to a CBV through multiple inheritance, letting you compose functionality without duplicating code across views.
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.views.generic import ListView, CreateView
# LoginRequiredMixin - restricts a view to authenticated users
class MyBooksView(LoginRequiredMixin, ListView):
model = Book
template_name = 'my_books.html'
login_url = '/login/' # where to redirect if not logged in
redirect_field_name = 'next'
def get_queryset(self):
return Book.objects.filter(owner=self.request.user)
# PermissionRequiredMixin - restricts to users with a specific permission
class BookCreateView(PermissionRequiredMixin, CreateView):
model = Book
fields = ['title', 'price']
permission_required = 'myapp.add_book'
# Mixin order MATTERS - Django resolves attributes/methods left to right (MRO)
# Mixins should generally come BEFORE the base generic view class
class SecureBookListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
model = Book
permission_required = 'myapp.view_book'
# Writing a CUSTOM mixin
class TitleMixin:
page_title = 'Default Title'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) # cooperative inheritance
context['page_title'] = self.page_title
return context
class BookListView(TitleMixin, ListView):
model = Book
page_title = 'All Books' # overrides the mixin's default
# UserPassesTestMixin - custom authorization logic
from django.contrib.auth.mixins import UserPassesTestMixin
class OwnerOnlyView(UserPassesTestMixin, DetailView):
model = Book
def test_func(self):
return self.get_object().owner == self.request.user
Q6. How do you handle GET and POST request data in Django views?
| Data source | Access via | Typical use |
|---|---|---|
| Query string (?key=value) | request.GET | Search, filters, pagination |
| Form-encoded POST body | request.POST | Traditional HTML form submissions |
| JSON request body | json.loads(request.body) | API requests from JS/mobile clients |
| URL path segments | View function/method arguments | Resource identifiers (pk, slug) |
import json
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
# GET parameters - from the query string
def search_books(request):
query = request.GET.get('q', '') # returns '' if 'q' isn't present
page = request.GET.get('page', 1)
books = Book.objects.filter(title__icontains=query)
return render(request, 'search.html', {'books': books, 'query': query})
# URL: /search/?q=django&page=2
# POST data - from a traditional form submission
def create_book(request):
if request.method == 'POST':
title = request.POST.get('title')
price = request.POST.get('price')
Book.objects.create(title=title, price=price)
return redirect('book-list')
return render(request, 'book_form.html')
# request.FILES - for uploaded files (separate from request.POST)
def upload_cover(request):
if request.method == 'POST':
cover_file = request.FILES.get('cover')
book = Book.objects.get(pk=request.POST.get('book_id'))
book.cover = cover_file
book.save()
# JSON body - common for API-style endpoints
@csrf_exempt # only needed if CSRF middleware would otherwise block this
def api_create_book(request):
if request.method == 'POST':
data = json.loads(request.body) # request.POST is EMPTY for JSON bodies
book = Book.objects.create(title=data['title'], price=data['price'])
return JsonResponse({'id': book.id, 'title': book.title}, status=201)
# Checking the HTTP method explicitly
def flexible_view(request):
if request.method == 'GET':
return handle_get(request)
elif request.method == 'POST':
return handle_post(request)
return HttpResponseNotAllowed(['GET', 'POST'])
Q7. What is Django's CSRF protection and how does it work with views?
Django's CsrfViewMiddleware protects against Cross-Site Request Forgery by requiring a secret token to be present and valid on state-changing requests (POST, PUT, DELETE), rejecting requests that don't include it.
# In a template - the {% csrf_token %} tag inserts a hidden input
# book_form.html
<form method='post'>
{% csrf_token %}
<input type='text' name='title'>
<button type='submit'>Save</button>
</form>
# Without {% csrf_token %}, submitting this form returns:
# 403 Forbidden - CSRF verification failed
# For AJAX/JavaScript requests, the token must be sent as a header
# JS example
# function getCookie(name) { ... } // extract csrftoken cookie
# fetch('/api/books/', {
# method: 'POST',
# headers: { 'X-CSRFToken': getCookie('csrftoken') },
# body: JSON.stringify({title: 'New Book'})
# });
# Exempting a specific view from CSRF checks (use carefully!)
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def webhook_receiver(request):
# only exempt views that use a DIFFERENT authentication mechanism
# (e.g. signature verification for webhooks), never for regular forms
pass
# Class-based view CSRF exemption
from django.utils.decorators import method_decorator
@method_decorator(csrf_exempt, name='dispatch')
class WebhookView(View):
def post(self, request):
pass
# Django REST Framework handles CSRF differently - session-authenticated
# requests still need it, but token/API-key authenticated requests are exempt
# by DRF's authentication classes automatically
# Common cause of CSRF errors in development: forgetting {% csrf_token %}
# in a form, or making an AJAX POST without including the token header
Q8. How do you use get_object_or_404() and get_list_or_404() in Django views?
These shortcut functions fetch an object or list, automatically raising an Http404 exception (rendering a 404 page) instead of an unhandled DoesNotExist exception when nothing matches.
from django.shortcuts import get_object_or_404, get_list_or_404, render
# WITHOUT the shortcut - manual exception handling
def book_detail_manual(request, pk):
try:
book = Book.objects.get(pk=pk)
except Book.DoesNotExist:
from django.http import Http404
raise Http404('Book does not exist')
return render(request, 'book_detail.html', {'book': book})
# WITH get_object_or_404() - one line, same behavior
def book_detail(request, pk):
book = get_object_or_404(Book, pk=pk) # raises Http404 automatically if not found
return render(request, 'book_detail.html', {'book': book})
# Can also filter with multiple conditions, like .get()
def available_book_detail(request, pk):
book = get_object_or_404(Book, pk=pk, available=True)
return render(request, 'book_detail.html', {'book': book})
# Works with a QuerySet too, not just a Model class
def my_book_detail(request, pk):
book = get_object_or_404(request.user.books.all(), pk=pk)
return render(request, 'book_detail.html', {'book': book})
# get_list_or_404() - for lists, raises Http404 if the result is EMPTY
def author_books(request, author_id):
books = get_list_or_404(Book, author_id=author_id)
# unlike .filter() which returns an empty list silently,
# this raises Http404 if the author has zero books
return render(request, 'author_books.html', {'books': books})
# What the resulting 404 page looks like:
# In DEBUG=True: a detailed Django debug 404 page
# In DEBUG=False: your custom templates/404.html template
Q9. How do you organize URLs using include() and app namespacing in Django?
include() lets you delegate a URL prefix to another app's urls.py, keeping the project's main URL configuration modular and each app's URLs self-contained. Namespacing avoids name collisions between apps using the same URL name.
# project/urls.py - the root URL configuration
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', include('books.urls')), # delegates /books/* to the books app
path('accounts/', include('accounts.urls')), # delegates /accounts/* to accounts app
path('api/', include('api.urls')),
]
# books/urls.py - app-specific URLs
from django.urls import path
from . import views
app_name = 'books' # namespace for this app's URLs
urlpatterns = [
path('', views.book_list, name='list'), # full URL: /books/
path('<int:pk>/', views.book_detail, name='detail'), # /books/5/
]
# accounts/urls.py - could ALSO have a URL named 'detail' without conflict,
# because each is namespaced separately
app_name = 'accounts'
urlpatterns = [
path('<int:pk>/', views.profile_detail, name='detail'), # /accounts/5/
]
# Referencing namespaced URLs in templates - use 'app_name:url_name'
# {% url 'books:detail' pk=book.pk %}
# {% url 'accounts:detail' pk=user.pk %}
# Referencing namespaced URLs in Python code
from django.urls import reverse
url = reverse('books:detail', kwargs={'pk': 5}) # /books/5/
# Without namespacing, two apps both using name='detail' would clash -
# Django would resolve to whichever was registered LAST, silently breaking
# the other app's links
# Instance namespaces (for the same app included multiple times)
urlpatterns = [
path('author-books/', include('books.urls', namespace='author-books')),
path('reader-books/', include('books.urls', namespace='reader-books')),
]
Django Views & URL Routing
Build routing patterns. Differentiate class-based views (CBVs) from function-based views (FBVs), URL patterns, namespace redirects, and context processing.
What is the difference between function-based views (FBVs) and class-based views (CBVs) in Django?
AspectFunction-based viewsClass-based viewsDefinitionA plain Python functionA Python class extending View or a...
How does Django's URL routing work with path() and re_path()?
FunctionPattern syntaxUse casepath()Simple converters like <int:pk>Most common URLs - clean, readablere_path()...
What are Django's generic class-based views like ListView, DetailView, and CreateView?
Generic CBVs implement common view patterns (listing, detail display, create/update/delete forms) with minimal...
What is the difference between render(), redirect(), and HttpResponse in Django views?
Function/classPurposeStatus codeHttpResponse()Raw response with arbitrary content200 by defaultrender()Renders...
What are Django mixins and how do you use them with class-based views?
Mixins are reusable classes that add specific behavior to a CBV through multiple inheritance, letting you comp...
How do you handle GET and POST request data in Django views?
Data sourceAccess viaTypical useQuery string (?key=value)request.GETSearch, filters, paginationForm-encoded PO...
What is Django's CSRF protection and how does it work with views?
Django's CsrfViewMiddleware protects against Cross-Site Request Forgery by requiring a secret token to be pres...
How do you use get_object_or_404() and get_list_or_404() in Django views?
These shortcut functions fetch an object or list, automatically raising an Http404 exception (rendering a 404...
How do you organize URLs using include() and app namespacing in Django?
include() lets you delegate a URL prefix to another app's urls.py, keeping the project's main URL configuratio...