Introduction to Django Class-Based Views
This article introduces Django’s class-based views and explains how they organize request-handling logic using Python classes. It covers the base View class, HTTP method handlers, generic views, URL configuration, common attributes and methods, mixins, CRUD patterns, and situations where class-based views are preferable to function-based views.
Introduction to Django Class-Based Views
Django views receive web requests and return web responses.
A view might render an HTML template, process a form, redirect the user, return JSON, or retrieve data from the database.
Django supports two main styles of views:
- function-based views
- class-based views
A function-based view is written as a Python function:
⧉
1 2 3 4 5 | |
A class-based view is written as a Python class:
⧉
1 2 3 4 5 6 7 | |
Both examples can produce the same response.
The difference is how the code is structured.
Class-based views organize request-handling behavior into classes and methods. They also make it possible to reuse common view logic through inheritance, mixins, and Django’s built-in generic views.
What Is a Class-Based View?
A class-based view is a Python class that handles HTTP requests.
Instead of checking the request method manually, a class-based view usually defines methods such as:
⧉
1 2 3 4 5 | |
Django calls the method that matches the incoming HTTP request.
For example:
⧉
1 2 3 4 5 6 7 | |
When the browser sends a GET request, Django calls the view’s get() method.
A class-based view must be converted into a callable view before it can be used in a URL pattern.
This is done with as_view():
⧉
1 2 3 4 5 6 7 8 | |
The important part is:
⧉
1 | |
Do not pass the class directly:
⧉
1 | |
Why Use Class-Based Views?
Class-based views can reduce repeated code and provide a predictable structure for common request patterns.
They are especially useful when several views perform similar operations.
Common examples include:
- displaying a list of objects
- displaying one object
- creating a record
- updating a record
- deleting a record
- handling both
GETandPOSTrequests - applying reusable authentication or permission logic
Django includes generic class-based views for many of these tasks.
For example, a list page can often be written with only a few lines:
⧉
1 2 3 4 5 6 7 | |
Django can use this declaration to:
- query all
Articleobjects - choose a default template name
- provide the objects to the template
- return the rendered response
This removes much of the repeated code found in ordinary list views.
Function-Based Views and Class-Based Views
A function-based view places the request-handling logic inside a function.
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
The equivalent class-based view could be:
⧉
1 2 3 4 5 6 7 8 9 | |
Neither style is always better.
Function-based views are often easier when:
- the view is small
- the request flow is unusual
- the logic does not match a generic pattern
- direct control is more important than reuse
Class-based views are often useful when:
- the view follows a common pattern
- several views share behavior
- HTTP methods need separate handlers
- built-in generic views remove repeated code
- mixins can provide reusable functionality
The best choice depends on the complexity and structure of the view.
The Base View Class
The simplest class-based view inherits from django.views.View.
⧉
1 | |
The class can define methods for supported HTTP request types.
⧉
1 2 3 4 5 6 7 8 9 10 | |
A GET request calls:
⧉
1 | |
A POST request calls:
⧉
1 | |
This gives each request method its own section of code.
Handling GET Requests
A GET request is normally used to retrieve or display information.
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
This view:
- receives a
GETrequest - retrieves all articles
- renders a template
- passes the articles into the template context
The URL pattern is:
⧉
1 2 3 4 5 6 7 8 | |
Handling POST Requests
A POST request is normally used to submit or change data.
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | |
The get() method displays an empty form.
The post() method:
- receives submitted data
- validates the form
- saves a valid article
- redirects the user
- redisplays invalid forms with errors
Separating GET and POST behavior into different methods can make the request flow easier to follow.
Using URL Parameters
Class-based views can receive values captured from the URL.
Suppose the URL includes an article ID:
⧉
1 2 3 4 5 | |
The value is passed to the view method as a keyword argument:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
The captured value can also be accessed through:
⧉
1 | |
For example:
⧉
1 | |
The as_view() Method
Django’s URL system expects a callable object.
A class itself is not the final callable view, so Django provides as_view().
⧉
1 | |
The as_view() method creates a callable that:
- receives the request
- creates an instance of the view class
- stores request information on that instance
- determines the HTTP request method
- calls the matching method, such as
get()orpost() - returns the response
This is why the class-based view is registered like this:
⧉
1 2 3 4 5 | |
Useful View Attributes
A class-based view instance has several useful attributes.
self.request
Contains the current request object.
⧉
1 | |
self.args
Contains positional arguments passed from the URL.
⧉
1 | |
Keyword URL arguments are more common in modern Django projects.
self.kwargs
Contains named values captured from the URL.
⧉
1 | |
These attributes are available after Django initializes the view.
Generic Class-Based Views
Django provides built-in class-based views for common web application patterns.
These are called generic class-based views.
Common generic views include:
| View | Purpose |
|---|---|
TemplateView |
Render a template |
RedirectView |
Redirect to another URL |
ListView |
Display a list of objects |
DetailView |
Display one object |
CreateView |
Create an object |
UpdateView |
Update an object |
DeleteView |
Delete an object |
FormView |
Display and process a form |
These views provide common behavior that can be configured with class attributes or overridden methods.
TemplateView
TemplateView renders a template.
⧉
1 2 3 4 5 | |
URL pattern:
⧉
1 | |
This is useful for mostly static pages.
Adding Template Context
Override get_context_data() to add data:
⧉
1 2 3 4 5 6 7 8 9 10 | |
The template can access:
⧉
1 | |
Always call:
⧉
1 | |
This preserves the context created by the parent class.
ListView
ListView displays a collection of objects.
⧉
1 2 3 4 5 6 7 | |
By default, Django looks for a template named:
⧉
1 | |
The pattern is:
⧉
1 | |
The default context variable is:
⧉
1 | |
The template can use:
⧉
1 2 3 | |
Customizing the Template and Context Name
⧉
1 2 3 4 | |
The template can now use:
⧉
1 2 3 | |
Customizing the Queryset
Override get_queryset():
⧉
1 2 3 4 5 6 7 8 9 | |
This is useful for:
- filtering records
- ordering results
- limiting results
- filtering by the current user
- using URL values in a query
For example:
⧉
1 2 3 4 | |
DetailView
DetailView displays one object.
⧉
1 2 3 4 5 6 7 | |
A common URL pattern is:
⧉
1 2 3 4 5 | |
By default, Django looks for:
⧉
1 | |
The object is available as:
⧉
1 | |
It may also be available using the model name:
⧉
1 | |
Example template:
⧉
1 2 3 | |
Using a Slug
A detail view can retrieve an object by slug:
⧉
1 2 3 4 | |
URL pattern:
⧉
1 2 3 4 5 | |
CreateView
CreateView displays a form and creates a new model object.
⧉
1 2 3 4 5 6 7 8 9 10 11 | |
This view handles:
- displaying the form
- validating submitted data
- saving the object
- redisplaying form errors
- redirecting after success
The form is available in the template as:
⧉
1 | |
Example template:
⧉
1 2 3 4 5 | |
Setting Values Before Saving
Override form_valid():
⧉
1 2 3 4 5 6 7 8 9 | |
This sets the current user as the article’s author before the object is saved.
UpdateView
UpdateView edits an existing object.
⧉
1 2 3 4 5 6 7 8 9 10 11 | |
URL pattern:
⧉
1 2 3 4 5 | |
UpdateView retrieves the object, fills the form with its current data, validates changes, and saves the updated object.
DeleteView
DeleteView displays a confirmation page and deletes an object after a POST request.
⧉
1 2 3 4 5 6 7 8 9 10 | |
URL pattern:
⧉
1 2 3 4 5 | |
Example confirmation template:
⧉
1 2 3 4 5 6 7 8 | |
Deletion should normally be performed through POST, not GET.
FormView
FormView handles forms that are not directly tied to creating or updating a model.
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Use FormView for forms such as:
- contact forms
- search forms
- feedback forms
- subscription forms
- custom workflow forms
Common Class Attributes
Generic views are often configured through class attributes.
| Attribute | Purpose |
|---|---|
model |
Model used by the view |
template_name |
Template to render |
context_object_name |
Name used in template context |
queryset |
Base queryset |
fields |
Model fields included in a generated form |
form_class |
Custom form class |
success_url |
Redirect destination after success |
slug_field |
Model field used for slug lookup |
slug_url_kwarg |
URL keyword containing the slug |
paginate_by |
Number of objects per page |
Example:
⧉
1 2 3 4 5 | |
Common Methods to Override
Class-based views can be customized by overriding methods.
Common methods include:
| Method | Purpose |
|---|---|
get_queryset() |
Return the objects used by the view |
get_context_data() |
Add values to template context |
get_object() |
Retrieve the main object |
get_form() |
Return the form instance |
get_form_kwargs() |
Add arguments passed to the form |
form_valid() |
Handle a valid form |
form_invalid() |
Handle an invalid form |
get_success_url() |
Determine the success redirect |
dispatch() |
Handle the request before method routing |
Overriding get_context_data()
Use get_context_data() to add extra template data.
⧉
1 2 3 4 5 6 7 8 9 | |
The template can use:
⧉
1 2 3 | |
Overriding get_queryset()
Use get_queryset() to control which objects are available.
⧉
1 2 3 4 5 6 7 8 | |
For user-specific data:
⧉
1 2 3 4 | |
Overriding get_success_url()
Use get_success_url() when the redirect depends on the saved object.
⧉
1 2 3 4 5 6 7 8 9 10 11 12 | |
After creating the article, the user is redirected to that article’s detail page.
reverse() and reverse_lazy()
Class attributes are evaluated when the module is imported.
For this reason, reverse_lazy() is commonly used for attributes such as success_url:
⧉
1 2 3 4 | |
Use reverse() inside methods:
⧉
1 2 3 4 5 6 7 8 | |
A simple rule is:
⧉
1 2 | |
Mixins
A mixin is a class that adds reusable behavior to another class.
Django provides mixins for common requirements such as authentication and permissions.
LoginRequiredMixin
Require the user to be logged in:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 | |
Mixins normally appear before the main generic view:
⧉
1 2 3 4 5 | |
UserPassesTestMixin
Apply a custom permission test:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
This allows only the article’s author to edit it.
PermissionRequiredMixin
Require a Django permission:
⧉
1 2 3 4 5 6 7 8 9 10 | |
Method Resolution Order
Class-based views often inherit from several classes.
Python uses the method resolution order, or MRO, to decide which implementation runs first.
This matters when using mixins:
⧉
1 2 3 4 5 | |
The order of parent classes can affect behavior.
As a general rule:
- place mixins first
- place the main Django view class last
- call
super()when overriding cooperative methods
Example:
⧉
1 2 3 4 | |
Failing to call super() may remove context or behavior provided by parent classes.
A Complete CRUD Example
Consider this model:
⧉
1 2 3 4 5 6 7 8 9 10 11 | |
The views can be written as:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | |
The URL patterns are:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | |
Together, these views provide basic create, read, update, and delete behavior.
Common Beginner Mistakes
Forgetting as_view()
Incorrect:
⧉
1 | |
Correct:
⧉
1 | |
Using the Wrong URL Parameter Name
Generic detail views expect pk by default:
⧉
1 2 3 4 | |
Using another name requires configuration:
⧉
1 2 3 | |
Forgetting to Call super()
Incorrect:
⧉
1 2 | |
This removes context created by the parent view.
Correct:
⧉
1 2 3 4 | |
Overriding Too Much
A generic view may already provide the needed behavior.
Before overriding a method, check whether a class attribute is enough.
Prefer:
⧉
1 2 3 | |
over rewriting the entire get() method without a clear reason.
Using Class-Based Views for Every View
Class-based views are not mandatory.
A small or unusual request flow may be clearer as a function-based view.
Use the style that makes the behavior easiest to understand.
Hiding Too Much Logic in Mixins
Mixins can reduce repetition, but too many mixins can make it difficult to determine where behavior comes from.
Keep inheritance structures understandable.
When to Use Class-Based Views
Class-based views are a good choice when:
- the view follows a standard pattern
- built-in generic views match the task
- several views share behavior
- authentication or permissions can be added with mixins
- HTTP methods benefit from separate handlers
- configuration through class attributes keeps the view simple
Function-based views may be clearer when:
- the view has a short custom workflow
- the logic does not match a generic view
- several unrelated actions happen in one request
- inheritance would make the flow harder to understand
Recommended Learning Approach
Class-based views can feel difficult because their behavior is distributed across parent classes.
A practical learning order is:
- Learn the base
Viewclass. - Write separate
get()andpost()methods. - Learn
TemplateView. - Learn
ListViewandDetailView. - Learn
CreateView,UpdateView, andDeleteView. - Practice overriding
get_queryset(). - Practice overriding
get_context_data(). - Add authentication and permission mixins.
- Inspect parent classes only when customization is required.
Do not try to memorize every method.
Start with the generic view that matches the task, configure its main attributes, and override only the behavior that needs to change.
Mini Reference
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Common configuration:
⧉
1 2 3 4 5 6 7 8 | |
Common methods:
⧉
1 2 3 4 5 6 7 8 9 | |
URL registration:
⧉
1 2 3 4 5 | |
Django class-based views organize view behavior into Python classes.
The base View class separates request methods such as GET and POST, while generic views provide reusable implementations for common tasks such as listing, displaying, creating, updating, and deleting objects.
The main ideas to remember are:
- class-based views are classes that handle requests
as_view()converts the class into a callable view- HTTP methods are handled by methods such as
get()andpost() - generic views provide common application patterns
- class attributes configure standard behavior
- methods can be overridden when customization is needed
- mixins add reusable authentication and permission behavior
super()preserves behavior from parent classes
Class-based views are most useful when they simplify common patterns. They should reduce repetition and clarify structure, not make a straightforward view harder to understand.
Join the Newsletter
Practical insights on Django, backend systems, deployment, architecture, and real-world development — delivered without noise.
Get updates when new guides, learning paths, cheat sheets, and field notes are published.
No spam. Unsubscribe anytime.
There is no third-party involved so don't worry - we won't share your details with anyone.