Skip to main content

Command Palette

Search for a command to run...

Understanding Django’s Class-Based Views: Generic Views

Published
4 min read
Understanding Django’s Class-Based Views: Generic Views

Hello there!

Welcome to the second part of this article series on understanding Django’s class-based views. In the previous article, I explained the basic View class of Django, which is the foundational class-based view for every other view I’ll be talking about going forward.

In this article, I’ll be talking about generic views using the ListView class to explain the things Django abstracts behind the scenes.

I found out that generic views in Django are not commonly used when building APIs — which is what I’m mostly concerned about — but I still decided to at least talk about the abstractions behind one of these generic views.

When you write a view class by subclassing the ListView class, it is generally used to render an HTML template. But what really happens behind the scenes? In Django, the ListView class itself simply inherits from two different classes: BaseListView and MultipleObjectTemplateResponseMixin. It doesn’t do anything special on its own; it just combines the functionalities of these two classes.

The BaseListView class inherits from Django’s basic View (which I talked about in my previous article) and from MultipleObjectMixin, which is different from MultipleObjectTemplateResponseMixin. And this is where things get interesting — BaseListView adds a special functionality that makes it powerful.

BaseListView Class provides the core logic that ListViewsits on. The get() method inside the class is responsible for fetching the queryset, validating it, creating the context, and finally returning the response that the browser receives. Understanding this method helps you see exactly what Django is doing behind the scenes whenever you use the ListView generic class.

BaseListView class:

Now, let me break down this code into bits.

The first thing the view does is fetch the queryset:

This self.get_queryset() method is inherited from MultipleObjectMixin and is responsible for returning a collection of objects, usually a QuerySet such as Model.objects.all(). That queryset is then stored on the view instance as self.object_list, which makes it accessible to other methods.

Next, Django checks whether it’s acceptable to return an empty list:

The allow_empty attribute determines whether a 404 error should be raised when the queryset is empty. By default, ListView allows empty lists, but if you explicitly set allow_empty = False, then Django must ensure that the queryset contains at least one object. You would normally do this when there are cases when you would not want an empty list, like when you have a page that is supposed to show a list of events. If there are no events, the page should not load at all. Instead, Django should return a 404 error indicating that the page is not found.

This leads to the emptiness-checking logic:

Django handles two different scenarios:

  1. Paginated QuerySets
    If pagination is being used, Django checks whether there is at least one item in the database by using a very quick “does anything exist?” check. It doesn’t load all the records — it just asks the database if at least one item is there.

  2. Plain lists or unpaginated results
    If the result is just a normal Python list or something similar, Django simply checks whether the list is empty or not. If the list has items, it’s fine; if it’s empty, Django treats it as empty.

    If the list is empty and empty lists are not allowed(set to false), Django raises simply raises a 404. And if the list isn’t empty, Django proceeds to prepare the context that will be passed to the template:

    The get_context_data() method (also provided by MultipleObjectMixin) is used to collect or get the object_list and other useful variables such as pagination information, which is then stored on the context variable.

    Finally, the method returns the rendered response:

    This part comes from MultipleObjectTemplateResponseMixin. It loads the appropriate template, injects the context, and generates the final HTML response that is sent back to the client.

    In summary, ListView class does all the heavy lifting by inheriting from other classes and mixins behind the scene: retrieving objects, validating them, preparing context, and rendering the response. When you use ListView, you're really relying on logic of these other classes and mixins — ListView simply brings together BaseListView and the template-rendering abilities of MultipleObjectTemplateResponseMixin.

Thank you for sticking through to the end. Note that I didn’t really do much of a deep dive on this topic since it doesn’t directly relate to building APIs, which is what I’m mainly concerned about.

Anyway, see you in my next article where we’ll finally start looking at class-based views in Django REST Framework.