Introduction
As your Django projects grow, writing everything with function-based views can become repetitive. Many pages follow common patterns such as displaying a template, listing objects, showing details, creating data, updating data, or deleting data. Django solves this by offering Class-Based Views (CBVs).
Class-Based Views help you write cleaner, more reusable, and more organized code. Instead of defining a view as a simple function, you define it as a class. Django then uses built-in methods to handle requests in a structured way.
In this tutorial, you will learn what Class-Based Views are, why they are useful, how they work, and how to use some of the most common ones in real Django projects.
What You Will Learn
By the end of this tutorial, you will understand:
- what Class-Based Views are
- the difference between function-based views and class-based views
- how to create basic CBVs
- how to use
TemplateView - how to use
ListView - how to use
DetailView - how to pass context data in CBVs
- when to use CBVs and when function-based views may still be better
Prerequisites
Before starting this tutorial, you should already know:
- Django project structure
- URLs and routing
- Django views basics
- templates
- models
- passing data from views to templates
1. What Are Class-Based Views?
In Django, a view is responsible for receiving a request and returning a response.
Until now, you have probably used function-based views, like this:
from django.http import HttpResponse
def home(request):
return HttpResponse("Hello from a function-based view!")A Class-Based View does the same job, but with a class:
from django.views import View
from django.http import HttpResponse
class HomeView(View):
def get(self, request):
return HttpResponse("Hello from a class-based view!")Here:
HomeViewis a class- it inherits from
View - the
get()method handles GET requests
2. Why Use Class-Based Views?
Class-Based Views are useful because they:
- reduce repetitive code
- make views more reusable
- organize code better in larger projects
- provide built-in generic views for common tasks
- allow inheritance and customization
For example, instead of writing the same logic again and again for listing database objects, Django gives you ListView.
Instead of manually loading an object by ID and rendering its detail page, Django gives you DetailView.
3. Function-Based Views vs Class-Based Views
Function-Based View Example
from django.shortcuts import render
from .models import Post
def post_list(request):
posts = Post.objects.all()
return render(request, 'blog/post_list.html', {'posts': posts})Class-Based View Example
from django.views.generic import ListView
from .models import Post
class PostListView(ListView):
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'Both do a similar job, but the CBV version is shorter and more structured.
When FBVs feel simpler
Function-based views are often easier for:
- very small logic
- custom workflows
- beginner understanding
When CBVs are better
Class-based views are often better for:
- repeated patterns
- CRUD applications
- large and organized projects
- reusable logic through inheritance
4. Your First Basic Class-Based View
Let us create a simple CBV manually.
In views.py
from django.views import View
from django.http import HttpResponse
class HelloView(View):
def get(self, request):
return HttpResponse("Hello, this is a basic Class-Based View!")In urls.py
from django.urls import path
from .views import HelloView
urlpatterns = [
path('hello/', HelloView.as_view(), name='hello'),
]Important Note: as_view()
When using a Class-Based View in urls.py, you must write:
HelloView.as_view()and not just:
HelloViewas_view() converts the class into a callable view that Django can use.
5. Handling Different HTTP Methods
One big advantage of CBVs is that you can separate logic by HTTP method.
from django.views import View
from django.http import HttpResponse
class ContactView(View):
def get(self, request):
return HttpResponse("Display the contact form")
def post(self, request):
return HttpResponse("Process the submitted form")This is much cleaner than putting all GET and POST logic inside one function.
6. Using TemplateView
TemplateView is one of the simplest generic CBVs. It is used when you want to display a template page.
Example
In views.py
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = 'about.html'In urls.py
from django.urls import path
from .views import AboutView
urlpatterns = [
path('about/', AboutView.as_view(), name='about'),
]In templates/about.html
<h1>About Us</h1>
<p>Welcome to our Django website.</p>This is perfect for pages like:
- About
- Contact
- FAQ
- Terms and Conditions
when no complex backend logic is needed.
7. Passing Context Data in TemplateView
Sometimes you want to send extra data to the template.
You can do that by overriding get_context_data().
Example
In views.py
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = 'about.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['site_name'] = 'My Django Blog'
context['author'] = 'Youssef'
return contextIn about.html
<h1>About {{ site_name }}</h1>
<p>Created by {{ author }}</p>Why super()?
super().get_context_data(**kwargs) keeps the default context from Django, then you add your own variables.
8. Using ListView
ListView is used to display a list of objects from the database.
This is very common for:
- blog posts
- products
- categories
- users
- articles
Let us suppose we have this model:
In models.py
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.titleBasic ListView
In views.py
from django.views.generic import ListView
from .models import Post
class PostListView(ListView):
model = Post
By default, Django will:
- use the model
Post - load all posts
- look for a template named
app_name/post_list.html - provide the object list in the template
9. Customizing ListView
Usually, you want more control.
from django.views.generic import ListView
from .models import Post
class PostListView(ListView):
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'
ordering = ['-created_at']What these mean
model = Post→ tells Django which model to usetemplate_name→ tells Django which template to rendercontext_object_name = 'posts'→ the template variable becomespostsordering = ['-created_at']→ shows newest posts first
10. Template for ListView
In templates/blog/post_list.html
<h1>All Posts</h1>
{% for post in posts %}
<h2>{{ post.title }}</h2>
<p>{{ post.content|truncatewords:20 }}</p>
<hr>
{% empty %}
<p>No posts found.</p>
{% endfor %}
11. Filtering Data in ListView
You can customize the queryset by overriding get_queryset().
Example: Only latest posts
from django.views.generic import ListView
from .models import Post
class PostListView(ListView):
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'
def get_queryset(self):
return Post.objects.order_by('-created_at')Example: Search by title
from django.views.generic import ListView
from .models import Post
class PostSearchView(ListView):
model = Post
template_name = 'blog/post_search.html'
context_object_name = 'posts'
def get_queryset(self):
query = self.request.GET.get('q')
if query:
return Post.objects.filter(title__icontains=query)
return Post.objects.all()This is a powerful feature because it lets you adapt the data based on user input.
12. Using DetailView
DetailView is used to display one single object.
For example:
- one blog post
- one product
- one category
- one user profile
Example
In views.py
from django.views.generic import DetailView
from .models import Post
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
In 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'),
]
Here, <int:pk> passes the primary key of the post to the view.
13. Template for DetailView
In templates/blog/post_detail.html
<h1>{{ post.title }}</h1>
<p>{{ post.content }}</p>
<p>Published on: {{ post.created_at }}</p>When the user visits:
/posts/1/Django finds the Post with pk=1 and sends it to the template.
14. Using Slugs Instead of Primary Keys
Sometimes you want cleaner URLs like:
/posts/my-first-django-post/instead of:
/posts/1/You can do this with a slug field.
Update the model
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.titleUpdate the view
from django.views.generic import DetailView
from .models import Post
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
slug_field = 'slug'
slug_url_kwarg = 'slug'Update the URL
path('posts/<slug:slug>/', PostDetailView.as_view(), name='post_detail'),Now the detail page becomes more readable and SEO-friendly.
15. Adding Extra Context in DetailView
You can also override get_context_data() in a DetailView.
from django.views.generic import DetailView
from .models import Post
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['related_posts'] = Post.objects.exclude(id=self.object.id)[:3]
return contextNow your template can display related posts too.
16. Full Example: Blog with ListView and DetailView
models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.titleviews.py
from django.views.generic import ListView, DetailView
from .models import Post
class PostListView(ListView):
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'
ordering = ['-created_at']
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
slug_field = 'slug'
slug_url_kwarg = 'slug'urls.py
from django.urls import path
from .views import PostListView, PostDetailView
urlpatterns = [
path('posts/', PostListView.as_view(), name='post_list'),
path('posts/<slug:slug>/', PostDetailView.as_view(), name='post_detail'),
]post_list.html
<h1>Blog Posts</h1>
{% for post in posts %}
<h2>
<a href="{% url 'post_detail' post.slug %}">{{ post.title }}</a>
</h2>
<p>{{ post.content|truncatewords:25 }}</p>
<hr>
{% empty %}
<p>No posts available.</p>
{% endfor %}post_detail.html
<h1>{{ post.title }}</h1>
<p>{{ post.content }}</p>
<p><small>Published on {{ post.created_at }}</small></p>
<p>
<a href="{% url 'post_list' %}">Back to all posts</a>
</p>17. Common Attributes in Generic CBVs
Here are some very common attributes you will often use:
In ListView
modeltemplate_namecontext_object_nameorderingpaginate_by
In DetailView
modeltemplate_namecontext_object_nameslug_fieldslug_url_kwarg
These attributes save time and reduce code.
18. Common Methods in Class-Based Views
get_context_data()
Used to add extra variables to the template context.
get_queryset()
Used mainly in ListView to customize which objects are returned.
get_object()
Used in DetailView when you want to customize how one object is retrieved.
Example:
def get_object(self):
return Post.objects.get(slug=self.kwargs['slug'])Usually, Django handles this automatically, but it is good to know.
19. Advantages of Class-Based Views
CBVs offer many benefits:
Better code organization
Each view is structured inside a class.
Code reuse
You can inherit from built-in generic views or from your own custom base views.
Less repetitive code
Generic views remove a lot of boilerplate.
Easier extension
You can override only the parts you need.
Scales better
For medium and large projects, CBVs help keep code cleaner.
20. Disadvantages of Class-Based Views
CBVs are powerful, but they also have a few drawbacks:
Harder for beginners at first
They may look more complex than functions.
Inheritance can be confusing
Sometimes it is not obvious where behavior comes from.
Too much abstraction
For very simple cases, FBVs may feel easier to read.
21. When Should You Use CBVs?
Use CBVs when:
- you have repeated patterns
- you are building CRUD pages
- you want cleaner reusable code
- you are working on medium or large projects
Use FBVs when:
- the logic is very small
- the view is very custom
- you want simpler code for learning or clarity
In real Django projects, many developers use both depending on the situation.
22. Common Beginner Mistakes with CBVs
Forgetting as_view()
Wrong:
path('about/', AboutView, name='about')Correct:
path('about/', AboutView.as_view(), name='about')Forgetting to set template_name
Django may look for a default template path that does not exist.
Using wrong context variable names
If you set:
context_object_name = 'posts'then in the template use posts, not object_list.
Confusing pk and slug
Be sure your URL pattern matches what the view expects.
23. Practical Mini Project
Let us build a tiny article system with CBVs.
Goal
Create:
- an article list page
- an article detail page
- clean URLs with slugs
Model
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=150)
slug = models.SlugField(unique=True)
body = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.titleViews
from django.views.generic import ListView, DetailView
from .models import Article
class ArticleListView(ListView):
model = Article
template_name = 'articles/article_list.html'
context_object_name = 'articles'
ordering = ['-created_at']
class ArticleDetailView(DetailView):
model = Article
template_name = 'articles/article_detail.html'
context_object_name = 'article'
slug_field = 'slug'
slug_url_kwarg = 'slug'URLs
from django.urls import path
from .views import ArticleListView, ArticleDetailView
urlpatterns = [
path('articles/', ArticleListView.as_view(), name='article_list'),
path('articles/<slug:slug>/', ArticleDetailView.as_view(), name='article_detail'),
]
Template: article_list.html
<h1>Articles</h1>
{% for article in articles %}
<h2>
<a href="{% url 'article_detail' article.slug %}">
{{ article.title }}
</a>
</h2>
<p>{{ article.body|truncatewords:20 }}</p>
{% empty %}
<p>No articles yet.</p>
{% endfor %}Template: article_detail.html
<h1>{{ article.title }}</h1>
<p>{{ article.body }}</p>
<p><a href="{% url 'article_list' %}">Back to articles</a></p>This is a simple but real example of how CBVs save time.
24. Summary
In this tutorial, you learned that:
- Class-Based Views are views written as Python classes
- they help organize and reuse code
Viewis the base class for custom CBVsTemplateViewis useful for static pagesListViewis used to display lists of objectsDetailViewis used to show one objectget_context_data()helps pass extra dataget_queryset()helps customize results- CBVs are especially useful in CRUD-style applications
25. Final Code Comparison
Function-Based View
def post_list(request):
posts = Post.objects.all()
return render(request, 'blog/post_list.html', {'posts': posts})Class-Based View
class PostListView(ListView):
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'The CBV version is shorter, cleaner, and easier to extend.
26. Mini Quiz
1. What does CBV stand for?
A. Class Built View
B. Class-Based View
C. Custom Browser View
D. Class Backend View
2. What method is required in urls.py when using a CBV?
A. render()
B. get()
C. as_view()
D. dispatch()
3. Which generic view is best for displaying one object?
A. ListView
B. DetailView
C. TemplateView
D. FormView
4. Which generic view is best for displaying many objects?
A. DetailView
B. DeleteView
C. ListView
D. RedirectView
5. Which method is used to add extra context data?
A. get_object()
B. get_context_data()
C. post()
D. dispatch()
27. What Comes Next?
Now that you understand the basics of Class-Based Views, the perfect next step is:
Tutorial : Generic Class-Based Views
Subject: CreateView, UpdateView, DeleteView, and faster CRUD development.
These views will show you how to create complete database applications with much less code.
💬 Comments
No comments yet. Be the first to comment!
Login to comment.