Function-Based Views
Function-based views, also known as view functions or views for short, are Python functions that take a web request and return a web response. The view contains the logic your application needs to handle a request such as fetching data, applying rules, and returning content.
A view function must accept an HttpRequest object as its first argument and return an HttpResponse-like object (covered in next page).
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = f"<html><body>It is now {now}.</body></html>"
return HttpResponse(html)Mapping URLs
You connect a function-based view to a URL using your URLconf (urls.py). For example:
from django.urls import path
from . import views
urlpatterns = [
path('datetime/', views.current_datetime),
]Now whenever someone visits /datetime/, Django calls current_datetime() and returns whatever it sends back.
Returning Errors
Django provides specific subclasses for common HTTP errors, such as HttpResponseNotFound for 404 errors or HttpResponseForbidden for 403 errors. However, there is not a specialized subclass for every possible response code. Since many codes are rarely used, you can instead pass any status code directly into the HttpResponse constructor.
from django.http import HttpResponse, HttpResponseNotFound
def my_view(request):
# Returning a specific subclass
if not request.user.is_authenticated:
return HttpResponseNotFound("<h1>Page not found</h1>")
# Passing a status code directly for a "created" (201) response
return HttpResponse(status=201)Because 404 errors are the most common, Django offers a more convenient way to handle them by raising an Http404 exception. When you raise this exception, Django catches it and returns the standard error page for your project.
from django.http import Http404
from .models import Product
def product_detail(request, product_id):
try:
product = Product.objects.get(pk=product_id)
except Product.DoesNotExist:
raise Http404("Product does not exist")
return HttpResponse(f"Product: {product.name}")NOTE
When DEBUG = True, any message passed to the Http404 exception is displayed on the standard 404 debug template. These messages are helpful for debugging during development, but they are generally not suitable for display in a production environment.
View Decorators
Decorators allow you to add specific behavior to your views without changing the core logic. You apply them by placing the decorator name with an @ symbol above the function.
Restricting HTTP Methods
You can limit which HTTP methods a view accepts. If a request uses a method that is not allowed, Django returns 405 Method Not Allowed error (HttpResponseNotAllowed) automatically.
@require_http_methods(["GET", "POST"]): Only allows the listed methods.@require_GET(): Only allows GET requests.@require_POST(): Only allows POST requests.@require_safe(): Only allows GET and HEAD requests. These methods are commonly considered "safe" because they are intended only to retrieve the requested resource without performing any actions that change the state of the system.
from django.views.decorators.http import require_http_methods
@require_http_methods(["GET", "POST"])
def my_view(request):
# This code only runs if the request is GET or POST
return HttpResponse("Success")Authentication & Authorization
These decorators, located in django.contrib.auth.decorators, allow you to restrict access to your views based on the user's login status or permissions.
@login_required: Checks if the user is logged in. If not, it redirects them to the login page (defined insettings.LOGIN_URL), appending the current path as anextquery parameter.@permission_required(perm): Checks if the user has a specific permission. If they don't, it redirects to the login page.@user_passes_test(test_func): A flexible decorator that takes a callable. If the callable returnsFalse, the user is redirected.
from django.contrib.auth.decorators import login_required, permission_required
from django.http import HttpResponse
@login_required
def my_profile(request):
return HttpResponse("This is your profile.")
@permission_required("polls.add_choice", raise_exception=True)
def add_choice(request):
# Raises a 403 Forbidden if the user lacks the specific permission
return HttpResponse("You can add choices.")Security & Debugging
Django provides decorators to help secure your views or hide sensitive data from debug logs.
@sensitive_variables(): Hides the values of specific local variables in the traceback email sent when an error occurs.@sensitive_post_parameters(): Hides specific POST parameters (like passwords or credit card numbers) from error reports.@xframe_options_deny: Prevents the view from being rendered inside a frame or iframe, protecting against clickjacking.@xframe_options_sameorigin: Allows the view to be rendered in a frame only if the frame is on the same domain.
from django.views.decorators.debug import sensitive_post_parameters
from django.views.decorators.clickjacking import xframe_options_deny
@sensitive_post_parameters("password", "credit_card")
@xframe_options_deny
def sensitive_view(request):
return HttpResponse("Sensitive processing happening here.")Controlling Cache
Django also provides decorators to control how responses are cached.
@cache_page(timeout): Caches the entire response from a view for a specified number of seconds.@never_cache: Ensures a page is never cached by adding specific headers to the response.@cache_control(**kwargs): Allows fine-grained control over cache headers such aspublic,privateandmax_age.
from django.views.decorators.cache import cache_page
@cache_page(60 * 15) # cache for 15 minutes
def my_view(request):
...Conditional View Processing
These decorators help manage how Django handles conditional GET requests. They allow you to generate ETag and Last-Modified headers, which help browsers determine if they can use a cached version of a page.
@condition(etag_func=None, last_modified_func=None): Allows you to define functions that calculate the ETag or last modified timestamp for a view.@etag(etag_func): Specifically sets the ETag header using a provided function.@last_modified(last_modified_func): Specifically sets the Last-Modified header.@conditional_page(): Provides the same logic as Django'sConditionalGetMiddlewareto a specific view, ensuring it handles conditional requests correctly.
from django.views.decorators.http import last_modified
def latest_update(request):
return MyModel.objects.latest("updated_at").updated_at
@last_modified(latest_update)
def my_view(request):
...GZip Compression
The @gzip_page decorator from django.views.decorators.gzip compresses the response content if the browser supports GZip compression. This can significantly reduce the size of the data sent over the network. It also automatically sets the Vary header so that caches do not serve compressed content to browsers that cannot handle it.
Vary Headers
Decorators in django.views.decorators.vary allow you to control how a cache builds its cache key based on specific request headers.
@vary_on_cookie: Instructs the cache to vary the response based on the user's cookie.@vary_on_headers(*headers): Instructs the cache to vary the response based on specific headers likeUser-Agent.
URL Normalization
The @no_append_slash decorator from django.views.decorators.common allows a specific view to opt out of the standard APPEND_SLASH behavior. This is useful if you need a specific URL to work exactly as defined without Django trying to redirect it by adding a trailing slash.
CSRF Exemption
By default, Django protects POST requests against CSRF attacks. If you need to disable this protection for a specific view function, you can use the @csrf_exempt decorator.
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def my_view(request):
# This view will skip the CSRF token check
return HttpResponse("This is a CSRF-exempt response")Use this decorator with caution, as disabling CSRF protection can introduce significant security risks.
Combining Decorators
You can apply multiple decorators to a single view function by stacking them. They execute from top to bottom, meaning the decorator closest to the function is applied first, and the ones above it wrap the result.
from django.views.decorators.http import require_GET
from django.views.decorators.cache import cache_page
@require_GET
@cache_page(60 * 15)
def my_view(request):
# This view only accepts GET requests and is cached for 15 minutes
return HttpResponse("Hello, world")Asynchronous Views
If your view performs tasks like calling an external API or waiting on a slow database query, you can define it as an asynchronous function.
import asyncio
from django.http import HttpResponse
async def async_view(request):
await asyncio.sleep(1)
return HttpResponse("Hello, async world!")