Difficulty: Beginner
Django's generic views provide pre-built implementations for the most common view patterns. Instead of writing the same boilerplate code for listing objects, displaying details, creating forms, and deleting records, you can use generic views that handle all of this with minimal configuration.
TemplateView is the simplest generic view - it renders a template. You just specify template_name and optionally override get_context_data() to pass extra context. It is ideal for static pages like home, about, and FAQ pages that do not require database queries.
ListView renders a list of objects from a model. You specify the model, and Django automatically fetches all objects, passes them to the template as 'object_list' (and 'modelname_list'), and renders the template. You can customize the queryset by overriding get_queryset(), add pagination by setting paginate_by, and add extra context by overriding get_context_data().
DetailView displays a single object. It looks up the object using pk or slug from the URL parameters and passes it to the template as 'object' (and 'modelname'). It automatically returns a 404 if the object does not exist.
CreateView and UpdateView handle form creation and editing. They work with ModelForms automatically - just specify the model and fields, and Django generates the form, handles validation, and saves the object. On success, they redirect to the object's get_absolute_url() or a custom success_url. The template receives the form as 'form' in the context.
DeleteView displays a confirmation page on GET and deletes the object on POST. It requires a success_url to redirect to after deletion, since the object (and its get_absolute_url()) will no longer exist.
All generic views support customization through method overriding. get_queryset() customizes which objects to display. get_context_data() adds extra context to the template. form_valid() runs after form validation succeeds (useful for setting the author or sending notifications). get_success_url() determines where to redirect after a successful operation.
Generic views follow a predictable template naming convention. ListView looks for 'appname/modelname_list.html', DetailView looks for 'appname/modelname_detail.html', and form views look for 'appname/modelname_form.html'. You can override this with template_name. DeleteView looks for 'appname/modelname_confirm_delete.html'.
The power of generic views comes from combining them with mixins. LoginRequiredMixin + CreateView gives you an authenticated form view. UserPassesTestMixin + UpdateView restricts editing to the object owner. FormMessagesMixin + DeleteView adds success messages. This composability makes generic views extremely flexible without writing repetitive code.
from django.views.generic import ListView, DetailView
from .models import Article
class ArticleListView(ListView):
model = Article
template_name = 'blog/article_list.html'
context_object_name = 'articles' # default is 'object_list'
paginate_by = 10
ordering = ['-published_at']
def get_queryset(self):
return Article.objects.filter(is_published=True)
def get_context_data(self, kwargs):
context = super().get_context_data(kwargs)
context['total_count'] = Article.objects.filter(is_published=True).count()
return context
class ArticleDetailView(DetailView):
model = Article
template_name = 'blog/article_detail.html'
context_object_name = 'article' # default is 'object'
def get_queryset(self):
return Article.objects.filter(is_published=True)
ListView fetches and paginates a list of objects. DetailView fetches a single object by pk or slug. Both support get_queryset() to customize the query and get_context_data() to add extra template context.
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView, UpdateView
from django.urls import reverse_lazy
from .models import Article
class ArticleCreateView(LoginRequiredMixin, CreateView):
model = Article
fields = ['title', 'content', 'category']
template_name = 'blog/article_form.html'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
model = Article
fields = ['title', 'content', 'category']
template_name = 'blog/article_form.html'
def get_queryset(self):
# Users can only edit their own articles
return Article.objects.filter(author=self.request.user)
CreateView auto-generates a ModelForm from the model and fields list. form_valid() is called after validation - here we set the author before saving. UpdateView uses the same pattern but pre-fills the form with existing data.
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import DeleteView
from django.urls import reverse_lazy
from .models import Article
class ArticleDeleteView(LoginRequiredMixin, DeleteView):
model = Article
template_name = 'blog/article_confirm_delete.html'
success_url = reverse_lazy('blog:article-list')
def get_queryset(self):
return Article.objects.filter(author=self.request.user)
# Template: blog/article_confirm_delete.html
# <form method="post">
# {% csrf_token %}
# <p>Are you sure you want to delete "{{ article.title }}"?</p>
# <button type="submit">Confirm Delete</button>
# <a href="{% url 'blog:article-list' %}">Cancel</a>
# </form>
DeleteView shows a confirmation page on GET and deletes on POST. Use reverse_lazy() instead of reverse() for success_url because the URL needs to be evaluated at class definition time, not import time.
# blog/urls.py
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.ArticleListView.as_view(), name='article-list'),
path('create/', views.ArticleCreateView.as_view(), name='article-create'),
path('<int:pk>/', views.ArticleDetailView.as_view(), name='article-detail'),
path('<int:pk>/edit/', views.ArticleUpdateView.as_view(), name='article-update'),
path('<int:pk>/delete/', views.ArticleDeleteView.as_view(), name='article-delete'),
]
This is a complete CRUD URL configuration using generic views. Five URL patterns handle listing, creating, viewing, editing, and deleting articles. Remember to put 'create/' before '<int:pk>/' to avoid routing issues.
ListView, DetailView, CreateView, UpdateView, DeleteView, TemplateView