Skip to content

Django Views

The view layer is responsible for processing requests and returning responses — it is the core of Django’s business logic. This article covers how to write function-based views (FBV) and class-based views (CBV), how to use response objects such as HttpResponse, render, redirect, and JsonResponse, and how to work with the request object’s common attributes and file uploads.

The View Layer

What Is a View Function?

A view function (or simply “view”) belongs to Django’s view layer and is defined by default in views.py. It is a function that handles web request information and returns a response.

Defining a View Function

from django.http import HttpResponse
import datetime

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

# Every view function takes an HttpRequest object as its first argument, conventionally named "request".
# The name of the view function does not matter; Django does not require any particular naming convention.

How render Works Internally

render is a shortcut that combines Template and Context into one call:

from django.template import Template, Context

res = Template('<h1>{{ user }}</h1>')
con = Context({'user': {'username': 'jason', 'password': 123}})
ret = res.render(con)
print(ret)
return HttpResponse(ret)

The JsonResponse Object

JSON is a language-agnostic data format that enables data exchange between the front end and back end.

# JavaScript equivalents:
#   JSON.stringify()  ↔  json.dumps()
#   JSON.parse()      ↔  json.loads()

import json
from django.http import JsonResponse

def ab_json(request):
    user_dict = {'username': 'json666', 'password': '123', 'hobby': 'girl'}
    l = [111, 222, 333, 444, 555]

    # Option 1: manually serialize and return
    # json_str = json.dumps(user_dict, ensure_ascii=False)
    # return HttpResponse(json_str)

    # Option 2: use JsonResponse — reads source code for usage details
    # return JsonResponse(user_dict, json_dumps_params={'ensure_ascii': False})

    # To serialize non-dict objects, set safe=False
    # return JsonResponse(l, safe=False)
    # By default only dicts can be serialized; other types require safe=False

Form File Upload and Backend Handling

"""
To upload a file through an HTML form:
  1. method must be set to POST
  2. enctype must be set to multipart/form-data
"""
def ab_file(request):
    if request.method == 'POST':
        # request.POST only retrieves ordinary key/value data; files are not included
        print(request.FILES)  # retrieve file data
        # <MultiValueDict: {'file': [<InMemoryUploadedFile: example.jpg (image/jpeg)>]}>
        file_obj = request.FILES.get('file')  # file object
        print(file_obj.name)
        with open(file_obj.name, 'wb') as f:
            for line in file_obj.chunks():  # chunks() reads the file in pieces (recommended)
                f.write(line)

    return render(request, 'form.html')

Frontend HTML:

<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <p>username: <input type="text" name="username"></p>
    <p>file: <input type="file" name="file"></p>
    <input type="submit">
</form>

The request Object’s Common Methods and Attributes

request.method          # HTTP method: 'GET', 'POST', etc.
request.POST            # POST data (form-encoded)
request.GET             # Query string parameters
request.FILES           # Uploaded files
request.body            # Raw binary data sent by the browser
request.path            # URL path without query string, e.g. /app01/ab_file/
request.path_info       # Same as request.path in most cases
request.get_full_path() # Full path including query string, e.g. /app01/ab_file/?username=json

# Example output:
print(request.path)           # /app01/ab_file/
print(request.path_info)      # /app01/ab_file/
print(request.get_full_path()) # /app01/ab_file/?username=json

FBV vs. CBV

Django views can be written as functions (FBV — Function-Based Views) or as classes (CBV — Class-Based Views).

Function-Based View (FBV)

def index(request):
    return HttpResponse('index')

Class-Based View (CBV)

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('login/', views.MyLogin.as_view()),
]

# views.py
from django.views import View

class MyLogin(View):
    def get(self, request):
        return render(request, 'form.html')

    def post(self, request):
        return HttpResponse('POST method')

CBV automatically dispatches to the correct method based on the HTTP request method, without needing an if request.method == 'GET' branch.

CBV Source Code Analysis

Understanding how CBV works internally helps you use and extend it correctly.

The key insight is in urls.py:

path('login/', views.MyLogin.as_view())
# This is functionally equivalent to:
# path('login/', views.view)   # just like FBV
# CBV and FBV are the same at the routing level: both map a URL to a function's memory address.

as_view() is a class method (@classonlymethod) that returns a plain function called view:

@classonlymethod
def as_view(cls, **initkwargs):
    """
    cls is our own class, e.g. MyLogin.
    Main entry point for a request-response process.
    """
    def view(request, *args, **kwargs):
        self = cls(**initkwargs)  # instantiate our class
        # self = MyLogin(**initkwargs)  — creates an instance of our class
        return self.dispatch(request, *args, **kwargs)
        """
        When reading Python source code, always remind yourself of the
        attribute/method lookup order for OOP:
            1. Look on the instance itself
            2. Look on the class that produced the instance
            3. Look on parent classes (MRO)
        Rule: whenever you see "self.something" in source code,
              always ask yourself: who is "self" at this moment?
        """
    return view

The core of CBV is the dispatch method:

def dispatch(self, request, *args, **kwargs):
    # Convert the HTTP method to lowercase and check if it is allowed
    if request.method.lower() in self.http_method_names:
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        """
        Reflection: using a string to look up an attribute or method on an object.
            handler = getattr(instance_of_our_class, 'get', fallback_if_not_found)
            handler = the get() method defined in our class
        """
    else:
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)
    # Automatically calls the correct method (e.g. get or post)

Summary of CBV execution flow (memorize this — no source code needed):

  1. A request arrives; Django calls the view function returned by as_view().
  2. view instantiates our class and calls self.dispatch(request).
  3. dispatch converts request.method to lowercase (e.g. 'get').
  4. It uses getattr(self, 'get', fallback) — reflection — to find the corresponding method.
  5. It calls that method and returns the response.

This is why defining a get method on your CBV class means it is called for GET requests, and post for POST requests, with no extra conditional logic needed.

Last updated on