Understanding Django View Functions and Error Handling

This article explains how Django view functions work, how they process requests and return responses, how to handle errors using HttpResponse subclasses and Http404, how to customize error pages, and how to write asynchronous views for modern ASGI-based applications.

Django viewsHttpResponseHttp404

~3 min read · Updated Mar 14, 2026

Introduction

A view in Django is a Python function that receives a request and returns a response. This response can be an HTML page, a redirect, an image, an XML document, or any other type of content. Django does not impose restrictions on where view code must live; the only requirement is that it must be importable through the Python path. By convention, views are placed inside a file named views.py within an application directory.


A Simple View

The following example demonstrates a basic view that returns the current date and time as an HTML response:


from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = '<html lang="en"><body>It is now %s.</body></html>' % now
    return HttpResponse(html)

How This View Works

  • The HttpResponse class is imported from django.http, along with Python’s datetime module.
  • A function named current_datetime is defined. The name is arbitrary; Django does not require specific naming.
  • Every view receives an HttpRequest object as its first argument.
  • The function returns an HttpResponse containing HTML.

Django uses the TIME_ZONE setting to determine the default timezone. You may adjust this value in your project’s settings file.


Mapping URLs to Views

To display a view at a specific URL, you must define a URLconf. This configuration maps URL patterns to view functions. Django processes incoming requests by matching the requested path against these patterns.


Returning Errors

Django provides subclasses of HttpResponse for common HTTP error codes. For example, HttpResponseNotFound represents a 404 response:


from django.http import HttpResponse, HttpResponseNotFound

def my_view(request):
    if foo:
        return HttpResponseNotFound("<h1>Page not found</h1>")
    else:
        return HttpResponse("<h1>Page was found</h1>")

You can also specify a custom status code directly:


from django.http import HttpResponse

def my_view(request):
    return HttpResponse(status=201)

The Http404 Exception

Instead of manually returning a 404 response, Django allows you to raise the Http404 exception. Django catches this exception and displays the standard 404 error page.


from django.http import Http404
from django.shortcuts import render
from polls.models import Poll

def detail(request, poll_id):
    try:
        p = Poll.objects.get(pk=poll_id)
    except Poll.DoesNotExist:
        raise Http404("Poll does not exist")
    return render(request, "polls/detail.html", {"poll": p})

To customize the 404 page, create a template named 404.html at the root of your template directory. Django uses this template when DEBUG is set to False.


Customizing Error Views

You can override Django’s default error handlers by defining them in your root URLconf:


handler404 = "mysite.views.my_custom_page_not_found_view"
handler500 = "mysite.views.my_custom_error_view"
handler403 = "mysite.views.my_custom_permission_denied_view"
handler400 = "mysite.views.my_custom_bad_request_view"

To override CSRF errors, use the CSRF_FAILURE_VIEW setting.


Testing Custom Error Views

You can test custom error handlers by raising exceptions in a test view:


from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.test import SimpleTestCase, override_settings
from django.urls import path

def response_error_handler(request, exception=None):
    return HttpResponse("Error handler content", status=403)

def permission_denied_view(request):
    raise PermissionDenied

urlpatterns = [
    path("403/", permission_denied_view),
]

handler403 = response_error_handler

@override_settings(ROOT_URLCONF=__name__)
class CustomErrorHandlerTests(SimpleTestCase):
    def test_handler_renders_template_response(self):
        response = self.client.get("/403/")
        self.assertContains(response, "Error handler content", status_code=403)

Asynchronous Views

Django supports asynchronous views using Python’s async def syntax. These views run in an ASGI environment and can improve performance when handling concurrent operations.


import datetime
from django.http import HttpResponse

async def current_datetime(request):
    now = datetime.datetime.now()
    html = '<html lang="en"><body>It is now %s.</body></html>' % now
    return HttpResponse(html)

For more details, refer to Django’s documentation on asynchronous support.


Written & researched by Dr. Shahin Siami

Related Articles

Django Tasks Framework: A Complete Guide to Background Task Execution in Django 6.0

Django 6.0 introduces the Tasks framework, a built‑in system for defining and queuing background work outside the request–response cycle. This article explains how Tasks work, how to configure backends, how to define and enqueue tasks, how context works, and how to integrate third‑party worker systems for production environments.

Continue

Asynchronous Support in Django: A Complete Guide to Async Views, ORM, Middleware, Performance, and Safety

This article explains Django’s asynchronous (async) capabilities, including async views, ASGI support, middleware behavior, async ORM features, performance considerations, handling disconnects, and Django’s async safety protections. It also covers how to use sync_to_async(), async ORM methods, and how to safely run synchronous code in async environments.

Continue

Django System Check Framework: A Complete Guide to Writing, Registering, Running, and Testing System Checks

This article explains Django’s System Check Framework—a powerful mechanism for detecting configuration issues, validating project structure, and ensuring code quality. It covers how checks are executed, how to write custom checks, how messages work, how to register and tag checks, how to extend checks for fields and models, and how to write both unit and integration tests for system checks.

Continue

Django Signals: A Complete Guide to Listening, Connecting, Sending, and Managing Application Events

This article provides a comprehensive explanation of Django’s signal system—an event‑driven mechanism that allows decoupled applications to react to actions occurring elsewhere in the framework. It covers how to define receivers, connect signals, use decorators, handle specific senders, organize signal code, and follow best practices to avoid complexity.

Continue

Understanding Django Settings: Configuration, Environment Management, and Best Practices

This article provides a complete overview of Django’s settings system. It explains how settings files work, how to designate a settings module, how to use settings in your code, how to configure Django manually, how to secure sensitive settings, and how to work with custom default settings. It also covers the role of django.setup() for standalone scripts.

Continue

Serializing and Deserializing Django Objects: A Complete Guide to Django’s Serialization Framework

This article explains Django’s serialization framework, including how to serialize and deserialize model instances, work with subsets of fields, handle inherited models, use different serialization formats (JSON, XML, YAML, JSONL), and understand how relational fields are represented. It also covers DeserializedObject behavior and common pitfalls.

Continue