Skip to content

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).

python
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:

python
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.

python
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.

python
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.
python
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 in settings.LOGIN_URL), appending the current path as a next query 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 returns False, the user is redirected.
python
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.
python
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 as public, private and max_age.
python
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's ConditionalGetMiddleware to a specific view, ensuring it handles conditional requests correctly.
python
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 like User-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.

python
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.

python
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.

python
import asyncio
from django.http import HttpResponse

async def async_view(request):
    await asyncio.sleep(1)
    return HttpResponse("Hello, async world!")