Introduction
In the previous tutorial, you learned the basics of Class-Based Views (CBVs) and saw how they help organize Django code in a cleaner and more reusable way. Now it is time to go one step further with Generic Class-Based Views.
Generic Class-Based Views are pre-built views provided by Django for common tasks such as:
- displaying a list of objects
- showing the details of one object
- creating new objects
- updating existing objects
- deleting objects
These views save a lot of time because Django already handles much of the repeated logic for you. Instead of writing the same CRUD code again and again, you can use Django’s generic views and customize only what you need.
In this tutorial, you will learn how to use the most important generic views, especially:
ListViewDetailViewCreateViewUpdateViewDeleteView
By the end, you will be able to build a complete CRUD application with much less code.
What You Will Learn
In this tutorial, you will learn:
- what generic class-based views are
- why they are useful
- how to use
ListView - how to use
DetailView - how to use
CreateView - how to use
UpdateView - how to use
DeleteView - how to create a complete CRUD workflow
- how to customize templates, fields, success URLs, and context
Prerequisites
Before starting, you should already know:
- Django project structure
- URLs and routing
- models
- templates
- forms basics
- Class-Based Views basics
1. What Are Generic Class-Based Views?
Generic Class-Based Views are built-in Django views that solve very common problems.
For example:
ListViewdisplays multiple objectsDetailViewdisplays one objectCreateViewcreates a new objectUpdateViewedits an existing objectDeleteViewdeletes an object
Instead of writing all logic manually, Django gives you ready-made classes that already know how to do these jobs.
This is especially useful in CRUD applications.
CRUD means:
- Create
- Read
- Update
- Delete
2. Why Use Generic Views?
Generic views are helpful because they:
- reduce repetitive code
- speed up development
- make your code cleaner
- follow Django best practices
- are easy to customize
- work very well for database-driven websites
Without generic views, you often write the same patterns again and again. With them, many tasks become much shorter.
3. A Model for Our Examples
Throughout this tutorial, we will use a simple Post model.
models.py
from django.db import models
from django.urls import reverse
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.title
def get_absolute_url(self):
return reverse('post_detail', kwargs={'slug': self.slug})Explanation
titlestores the post titleslugcreates clean URLscontentstores the body textcreated_atstores the publication dateget_absolute_url()tells Django where to redirect after creating or updating an object
That last method is very useful with CreateView and UpdateView.
4. ListView — Display Many Objects
ListView is used when you want to display a list of records.
views.py
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 This Does
- fetches all
Postobjects - sends them to the template
- renders the template
blog/post_list.html - makes them available in the template as
posts - orders them from newest to oldest
urls.py
from django.urls import path
from .views import PostListView
urlpatterns = [
path('posts/', PostListView.as_view(), name='post_list'),
]Template: post_list.html
<h1>All Posts</h1>
{% for post in posts %}
<h2>
<a href="{% url 'post_detail' post.slug %}">{{ post.title }}</a>
</h2>
<p>{{ post.content|truncatewords:20 }}</p>
<hr>
{% empty %}
<p>No posts available.</p>
{% endfor %}
5. DetailView — Display One Object
DetailView is used to show one single object.
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'
slug_field = 'slug'
slug_url_kwarg = 'slug'urls.py
from django.urls import path
from .views import PostDetailView
urlpatterns = [
path('posts/<slug:slug>/', PostDetailView.as_view(), name='post_detail'),
]Template: post_detail.html
<h1>{{ post.title }}</h1>
<p>{{ post.content }}</p>
<p>Published on: {{ post.created_at }}</p>
<p>
<a href="{% url 'post_update' post.slug %}">Edit</a> |
<a href="{% url 'post_delete' post.slug %}">Delete</a>
</p>Explanation
When the user visits a URL like:
/posts/my-first-post/Django finds the Post whose slug matches my-first-post and displays it.
6. CreateView — Create a New Object
CreateView is used to create new objects through a form.
views.py
from django.views.generic import CreateView
from .models import Post
class PostCreateView(CreateView):
model = Post
template_name = 'blog/post_form.html'
fields = ['title', 'slug', 'content']Explanation
model = Posttells Django which model to usetemplate_nametells Django which template to usefieldsdefines which fields appear in the form
Because our model has get_absolute_url(), Django automatically redirects to the post detail page after successful creation.
urls.py
from django.urls import path
from .views import PostCreateView
urlpatterns = [
path('posts/create/', PostCreateView.as_view(), name='post_create'),
]Template: post_form.html
<h1>Create a New Post</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Save Post</button>
</form>
<p><a href="{% url 'post_list' %}">Back to posts</a></p>7. UpdateView — Edit an Existing Object
UpdateView is used to modify an existing object.
views.py
from django.views.generic import UpdateView
from .models import Post
class PostUpdateView(UpdateView):
model = Post
template_name = 'blog/post_form.html'
fields = ['title', 'slug', 'content']
slug_field = 'slug'
slug_url_kwarg = 'slug'urls.py
from django.urls import path
from .views import PostUpdateView
urlpatterns = [
path('posts/<slug:slug>/edit/', PostUpdateView.as_view(), name='post_update'),
]How It Works
- Django loads the existing post
- fills the form with its current values
- after submission, saves the changes
- redirects to the post’s detail page
Because we reused post_form.html, one template works for both create and update.
8. DeleteView — Delete an Object
DeleteView is used to remove an object, usually after a confirmation page.
views.py
from django.urls import reverse_lazy
from django.views.generic import DeleteView
from .models import Post
class PostDeleteView(DeleteView):
model = Post
template_name = 'blog/post_confirm_delete.html'
success_url = reverse_lazy('post_list')
slug_field = 'slug'
slug_url_kwarg = 'slug'Why reverse_lazy?
reverse_lazy() is often used in class-based views because URLs are resolved only when needed.
urls.py
from django.urls import path
from .views import PostDeleteView
urlpatterns = [
path('posts/<slug:slug>/delete/', PostDeleteView.as_view(), name='post_delete'),
]Template: post_confirm_delete.html
<h1>Delete Post</h1>
<p>Are you sure you want to delete "{{ post.title }}"?</p>
<form method="post">
{% csrf_token %}
<button type="submit">Yes, delete</button>
</form>
<p><a href="{% url 'post_detail' post.slug %}">Cancel</a></p>9. Full CRUD Example
Now let us put everything together.
views.py
from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
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'
class PostCreateView(CreateView):
model = Post
template_name = 'blog/post_form.html'
fields = ['title', 'slug', 'content']
class PostUpdateView(UpdateView):
model = Post
template_name = 'blog/post_form.html'
fields = ['title', 'slug', 'content']
slug_field = 'slug'
slug_url_kwarg = 'slug'
class PostDeleteView(DeleteView):
model = Post
template_name = 'blog/post_confirm_delete.html'
success_url = reverse_lazy('post_list')
slug_field = 'slug'
slug_url_kwarg = 'slug'urls.py
from django.urls import path
from .views import (
PostListView,
PostDetailView,
PostCreateView,
PostUpdateView,
PostDeleteView,
)
urlpatterns = [
path('posts/', PostListView.as_view(), name='post_list'),
path('posts/create/', PostCreateView.as_view(), name='post_create'),
path('posts/<slug:slug>/', PostDetailView.as_view(), name='post_detail'),
path('posts/<slug:slug>/edit/', PostUpdateView.as_view(), name='post_update'),
path('posts/<slug:slug>/delete/', PostDeleteView.as_view(), name='post_delete'),
]This is a complete CRUD system with generic views.
10. Using success_url
Sometimes you do not want to use get_absolute_url().
In that case, you can define success_url.
Example with CreateView
from django.urls import reverse_lazy
from django.views.generic import CreateView
from .models import Post
class PostCreateView(CreateView):
model = Post
template_name = 'blog/post_form.html'
fields = ['title', 'slug', 'content']
success_url = reverse_lazy('post_list')Now, after creating a post, Django redirects to the post list page instead of the detail page.
11. Customizing the Form
You can customize the form used in CreateView and UpdateView in two main ways:
Method 1: Use fields
fields = ['title', 'slug', 'content']This is simple and good for beginners.
Method 2: Use a custom form class
Example:
forms.py
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'slug', 'content']views.py
from django.views.generic import CreateView
from .models import Post
from .forms import PostForm
class PostCreateView(CreateView):
model = Post
form_class = PostForm
template_name = 'blog/post_form.html'Using a custom form is better when you need:
- custom labels
- custom widgets
- custom validation
- extra styling
12. Adding Extra Context Data
You can still use get_context_data() in generic views.
Example
class PostListView(ListView):
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['page_title'] = 'All Blog Posts'
return contextThen in the template:
<h1>{{ page_title }}</h1>13. Filtering Objects with get_queryset()
You can customize which objects appear in ListView.
Example: Only posts containing a keyword
class PostListView(ListView):
model = Post
template_name = 'blog/post_list.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()Now users can search with URLs like:
/posts/?q=django14. Common Template Names Used by Generic Views
Django has default template naming rules.
For example, if your app is called blog and model is Post, Django expects:
blog/post_list.htmlblog/post_detail.htmlblog/post_form.htmlblog/post_confirm_delete.html
You can follow these defaults or set template_name manually.
15. Important Attributes to Know
model
Defines the model the view works with.
template_name
Defines which template to render.
context_object_name
Defines the variable name used in the template.
fields
Defines which model fields appear in create/update forms.
success_url
Defines where to redirect after success.
slug_field
Defines which model field is used as slug.
slug_url_kwarg
Defines the URL parameter name for the slug.
16. Common Beginner Mistakes
Forgetting as_view()
Wrong:
path('posts/', PostListView, name='post_list')Correct:
path('posts/', PostListView.as_view(), name='post_list')Forgetting get_absolute_url() or success_url
If you use CreateView or UpdateView and define neither, Django may not know where to redirect.
Wrong URL parameter names
If your view expects slug, your URL must also use slug.
Correct:
path('posts/<slug:slug>/', PostDetailView.as_view(), name='post_detail')Missing CSRF token in forms
Always include:
{% csrf_token %}inside POST forms.
Using reverse() instead of reverse_lazy() in class attributes
In class-based views, reverse_lazy() is usually safer for attributes like success_url.
17. Function-Based View vs Generic View Example
Function-Based Create View
from django.shortcuts import render, redirect
from .models import Post
from .forms import PostForm
def post_create(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
form.save()
return redirect('post_list')
else:
form = PostForm()
return render(request, 'blog/post_form.html', {'form': form})Generic CreateView
from django.views.generic import CreateView
from .models import Post
class PostCreateView(CreateView):
model = Post
fields = ['title', 'slug', 'content']
template_name = 'blog/post_form.html'The generic version is much shorter.
18. When Should You Use Generic Views?
Generic views are ideal when:
- you are building CRUD pages
- you want faster development
- your logic matches common patterns
- you want cleaner and reusable code
They may be less suitable when:
- the page has very custom behavior
- the workflow is unusual
- you need very specific request handling
In those cases, function-based views or custom CBVs may be better.
19. Mini Project: Book Manager
Let us create another example with books.
models.py
from django.db import models
from django.urls import reverse
class Book(models.Model):
title = models.CharField(max_length=150)
author = models.CharField(max_length=100)
description = models.TextField()
slug = models.SlugField(unique=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('book_detail', kwargs={'slug': self.slug})
views.py
from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from .models import Book
class BookListView(ListView):
model = Book
template_name = 'books/book_list.html'
context_object_name = 'books'
class BookDetailView(DetailView):
model = Book
template_name = 'books/book_detail.html'
context_object_name = 'book'
slug_field = 'slug'
slug_url_kwarg = 'slug'
class BookCreateView(CreateView):
model = Book
template_name = 'books/book_form.html'
fields = ['title', 'author', 'description', 'slug']
class BookUpdateView(UpdateView):
model = Book
template_name = 'books/book_form.html'
fields = ['title', 'author', 'description', 'slug']
slug_field = 'slug'
slug_url_kwarg = 'slug'
class BookDeleteView(DeleteView):
model = Book
template_name = 'books/book_confirm_delete.html'
success_url = reverse_lazy('book_list')
slug_field = 'slug'
slug_url_kwarg = 'slug'This is the same pattern, just with a different model.
20. Summary
In this tutorial, you learned that:
- Generic Class-Based Views are built-in Django views for common tasks
ListViewdisplays multiple objectsDetailViewdisplays one objectCreateViewcreates new objectsUpdateViewedits existing objectsDeleteViewdeletes objects- generic views make CRUD development much faster
- you can customize templates, context, forms, querysets, and redirections
Generic views are one of Django’s most useful features because they let you build real applications with much less code.
21. Mini Quiz
1. Which generic view is used to create a new object?
A. ListView
B. UpdateView
C. CreateView
D. DeleteView
2. Which generic view is used to edit an object?
A. DetailView
B. UpdateView
C. TemplateView
D. ListView
3. Which generic view is used to delete an object?
A. DeleteView
B. RemoveView
C. DestroyView
D. EraseView
4. What method can be added to a model to tell Django where to redirect after creation?
A. get_url()
B. redirect_url()
C. get_absolute_url()
D. get_redirect_path()
5. What is usually used in DeleteView for redirecting after deletion?
A. get_absolute_url()
B. reverse_lazy()
C. render()
D. object_list
22. What Comes Next?
The perfect next tutorial after this is:
Tutorial: Django QuerySets and ORM Deep Dive
Subject: filtering, ordering, excluding, chaining queries, and efficient data retrieval.
That tutorial will help you understand how Django retrieves and manipulates data behind all these generic views.
💬 Comments
No comments yet. Be the first to comment!
Login to comment.