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:

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:

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:

Prerequisites

Before starting, you should already know:

1. What Are Generic Class-Based Views?

Generic Class-Based Views are built-in Django views that solve very common problems.

For example:

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:

2. Why Use Generic Views?

Generic views are helpful because they:

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

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

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

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

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:

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 context

Then 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=django

14. 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:

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:

They may be less suitable when:

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 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.