In the previous post, we examined why Django's class-based views (CBVs) are necessary and what advantages they have compared to function-based views (FBVs). In this post, we will take a closer look at the Basic View Class of Django, which can be considered the foundation of all CBVs.

“To truly understand CBVs, it is essential to grasp how django.views.View operates and its structure.”


1. What is a Django View Class?

All class-based views in Django use the django.views.View class as a base class, inheriting from it as follows:

from django.views import View

class MyView(View):
    def get(self, request, *args, **kwargs):
        # Logic to handle GET requests
        ...

    def post(self, request, *args, **kwargs):
        # Logic to handle POST requests
        ...

Characteristics of the View Class

  1. Separation of Methods by HTTP Method
    When a request comes in, Django automatically calls the corresponding function such as get(), post(), put(), and delete() through the internal dispatch() method.

    • Since you don't have to use conditional statements like “if request.method == 'GET': … else: …” like in FBVs, the code structure is cleaner.
  2. Inheritance and Extensibility
    You can inherit from the View class to create multiple child views, maximizing code reuse by placing common logic in the parent class and specific logic in the child classes.


2. Basic Structure and Principles of the View Class

Django View structure LEGO style

The Simplest Example

# views.py
from django.http import HttpResponse
from django.views import View

class HelloCBV(View):
    def get(self, request, *args, **kwargs):
        return HttpResponse("Hello, CBV!")
  • The get() method is executed when a GET request is made from the browser (e.g., entering the URL in the address bar, clicking on a link).

  • HttpResponse("Hello, CBV!"): Creates and returns a text-based response directly.

Connecting to urls.py

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

urlpatterns = [
    path('hello/', HelloCBV.as_view(), name='hello_cbv'),
]
  • The .as_view() method converts the CBV into a form that Django can understand.

  • When a GET request is made to http://example.com/hello/, the get() method of HelloCBV will be called.


3. How to Use the View Class and Practical Examples

In real projects, it is rare to simply return “one string.” Generally, you will often perform JSON responses, template rendering, or database queries. Here, we will delve into examples of JSON responses and template rendering.

3.1 Example of JSON Response Handling

# views.py
from django.http import JsonResponse
from django.views import View

class UserDataView(View):
    def get(self, request, *args, **kwargs):
        # Assume we receive user_id as a GET parameter
        user_id = request.GET.get('user_id', None)
        if not user_id:
            return JsonResponse({'error': 'No user_id provided'}, status=400)

        # In practice, perform DB lookup or similar
        user_data = {
            'id': user_id,
            'name': f'User_{user_id}',
            'role': 'admin'
        }
        return JsonResponse(user_data, status=200)

    def post(self, request, *args, **kwargs):
        # You can parse JSON or form data from the POST request body.
        # You can use request.POST, request.body, request.FILES, etc.
        # In a real service, write logic to parse JSON and save to DB
        new_user_data = {
            'id': 123,
            'name': 'NewUser',
            'role': 'member'
        }
        return JsonResponse(new_user_data, status=201)
  • JsonResponse: A convenient tool for generating JSON responses in Django.

  • request.GET, request.POST: You can retrieve GET/POST parameters from Django's HttpRequest object.

  • In practice, you would link to DB models or use serializers for more structured data handling.

3.2 Example of Template Rendering

# views.py
from django.shortcuts import render
from django.views import View

class GreetingView(View):
    def get(self, request, *args, **kwargs):
        context = {
            'title': 'Welcome to My Site',
            'greeting': 'Hello, this is a GreetingView!'
        }
        return render(request, 'greeting.html', context)
<!-- templates/greeting.html -->
<!DOCTYPE html>
<html>
<head>
  <title>{{ title }}</title>
</head>
<body>
  <h1>{{ greeting }}</h1>
</body>
</html>
  • The render() function finds the template file (greeting.html) and returns the rendered HTML as HttpResponse.

  • This way, you can easily handle template rendering within CBVs.


4. Extension and Reusability of View Classes

One of the biggest reasons why View classes are useful is “code reuse.” As projects grow, it becomes much more efficient to place commonly needed logic (e.g., logging, authentication checks, common data processing) in the parent class and inherit it in the child classes.

4.1 Placing Common Logic in the Parent Class

# views.py
from django.http import JsonResponse
from django.views import View

class BaseLoggingView(View):
    def dispatch(self, request, *args, **kwargs):
        # Execute common logic for all requests (GET/POST, etc.)
        print(f"[Log] {request.method} request at {request.path}")
        return super().dispatch(request, *args, **kwargs)

class ChildLoggingView(BaseLoggingView):
    def get(self, request, *args, **kwargs):
        data = {'message': 'GET response from child'}
        return JsonResponse(data)

    def post(self, request, *args, **kwargs):
        data = {'message': 'POST response from child'}
        return JsonResponse(data)
  • The dispatch() method: This is the key method that controls the execution of view logic before and after it is processed by Django.

  • In this example, we log every request before calling the actual get(), post(), etc. using super().dispatch().

4.2 Adding Functionality through Mixins

Django often employs the Mixin technique to selectively “mix in certain features.” For example, you can use LoginRequiredMixin (to check login status), PermissionRequiredMixin (for permission checks), and more in your CBVs.

from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import View
from django.http import JsonResponse

class SecuredView(LoginRequiredMixin, View):
    def get(self, request, *args, **kwargs):
        return JsonResponse({'message': 'You are authenticated.'})
  • Mixins will be covered in more detail later when discussing generic views or permission management, so for now, just keep in mind that functionality can be combined this way.

5. A Simple Comparison with FBVs

Category FBV (Function-Based View) CBV (Class-Based View)
Structure Written in function form Written in class & method form
HTTP Method Handling if request.method == 'GET': ... else: ... Separated by method such as get(), post()
Code Duplication Potential Complex logic may easily lead to duplicated code Common logic can be reused through inheritance and Mixins
Extensibility Common logic is often mixed within function body Suitable for complex requirements with class inheritance structure
Readability Favorable for simple prototypes More suitable for maintaining a clear structure as scale increases
  • FBVs are quick to write for small or simple features, while CBVs favor scalability and maintainability.

  • Using CBVs is particularly efficient for team projects or medium to large-scale projects.


6. Digging a Little Deeper: dispatch() and URL Parameters

Django View request flowchart

6.1 Why Override dispatch() Directly

  • Common Pre- and Post-Processing: You can check the DB connection, perform permission checks, and log requests right here each time a request comes in.

  • Method Mapping: Internally, it uses request.method.lower() to locate and call get(), post(), put(), etc. If you want to handle an additional method like PATCH in a custom manner, you can override dispatch() to implement it.

class CustomDispatchView(View):
    def dispatch(self, request, *args, **kwargs):
        print("Custom pre-processing logic here.")
        response = super().dispatch(request, *args, **kwargs)
        print("Custom post-processing logic here.")
        return response

6.2 Receiving URL Parameters

If you specify path parameters in urls.py, like <int:user_id>/, you can receive values through kwargs in the get() and post() methods of your CBV.

![Django View structure LEGO style](/media/whitedec/blog_img/view_class_lego_structure.webp)# urls.py
urlpatterns = [
    path('user/<int:user_id>/', UserDetailView.as_view(), name='user_detail'),
]

# views.py
class UserDetailView(View):
    def get(self, request, *args, **kwargs):
        user_id = kwargs.get('user_id')
        # Look up the DB using user_id
        return HttpResponse(f"User ID: {user_id}")
  • Just like with FBVs, you can extract URL parameters from kwargs.

7. A Richer Practical Example: Simple Bulletin Board

For those who want to see a more practical application of View classes, let's look at a simple example of creating a bulletin board list and detail pages. (In the next post, we will explore generic views that can simplify CRUD operations, but here we will demonstrate that it is possible even with the basic View class.)

# models.py - Example Model
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title
# views.py
from django.views import View
from django.shortcuts import render, get_object_or_404
from .models import Post

# Viewing list of posts
class PostListView(View):
    def get(self, request, *args, **kwargs):
        posts = Post.objects.order_by('-created_at')
        context = {'posts': posts}
        return render(request, 'post_list.html', context)

# Viewing detailed post
class PostDetailView(View):
    def get(self, request, *args, **kwargs):
        post_id = kwargs.get('pk')
        post = get_object_or_404(Post, pk=post_id)
        context = {'post': post}
        return render(request, 'post_detail.html', context)
<!-- post_list.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Post List</title>
</head>
<body>
    <h1>Post List</h1>
    <ul>
        {% for post in posts %}
            <li>
                <a href="{% url 'post_detail' post.id %}">{{ post.title }}</a>
            </li>
        {% endfor %}
    </ul>
</body>
</html>
<!-- post_detail.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{{ post.title }}</title>
</head>
<body>
    <h1>{{ post.title }}</h1>
    <p>{{ post.content }}</p>
    <p>Created on: {{ post.created_at }}</p>
</body>
</html>
# urls.py
from django.urls import path
from .views import PostListView, PostDetailView

urlpatterns = [
    path('posts/', PostListView.as_view(), name='post_list'),
    path('posts/<int:pk>/', PostDetailView.as_view(), name='post_detail'),
]
  • PostListView: Retrieves and passes all posts to the template (post_list.html) when a GET request is made.

  • PostDetailView: Finds a specific post using the URL parameter pk and passes it to the template (post_detail.html).

  • This shows how easily you can configure both list and detail pages using just the basic View class.

However, to handle the entire CRUD process, Create/Update/Delete logic is necessary. Django offers generic views that facilitate this, and we will explore this in the next article!


8. Conclusion and Preview of the Next Post

This wraps up the discussion on how the Basic View Class of Django operates and how it can be practically utilized. To summarize the key points:

  1. By inheriting from django.views.View, you can handle HTTP requests by overriding methods such as get() / post() / dispatch().

  2. By placing common logic in the parent class and redefining only what is necessary in the child views, you can maximize code reuse.

  3. You can create view logic easily for various scenarios such as template rendering, JSON responses, and DB queries.

  4. As demonstrated through practical examples (a simple bulletin board), you can certainly build projects using just the basic View class.
    However, to handle CRUD operations conveniently, learning Django's generic views will make things much easier.

In the next post, we will focus on the FormView class, which streamlines form processing. You will learn how to automate the process of “accepting user input, validating it, and showing error messages upon failure.”

The CBV series continues!

  • Part 3: “Easily Handling Forms with FormView”
  • Part 4: “Using ListView & DetailView”
  • Part 5: “Implementing CRUD with CreateView, UpdateView, DeleteView”
  • … (and more to come)

Read Previous Post


Additional Reference Materials

I hope this article has helped clarify the structure and usage of the basic View class. In the next issue, we will learn how to conveniently handle processes such as “form creation → validation → error handling → redirect on success” using the FormView.


“Django's View classes are a powerful tool that not only separate code by HTTP methods, but also dramatically improve scalability and maintainability in large scale projects.”

Let's continue to explore CBVs for more productive Django development!