Class-based views (CBVs) in Django are a method for structuring views using Python classes. They provide a more structured and extensible way to create views. CBVs are commonly used in Django for performing CRUD (Create, Read, Update, Delete) operations on database models. For instance, when you are building a blog application, you can use Class-based views to create new blog posts effortlessly. Users submit a form, and the CBV handles the database insertion. In the context of an e-commerce website, CBVs can be employed to display product details or a list of products and update user data on profile pages. When working on Django projects, consider where CRUD operations are needed and how CBVs can offer a ready-made solution.
In this article, we'll demonstrate how to use CBVs in a Django project called "Football," with models Footballer, Match, and Tournament in the "Team" application. But first, you need to know the structure of the project.
football/. # Root project folder
manage.py # Django project management
football/ # Subfolder with Django project settings
__init__.py
settings.py # Project settings
urls.py # Main project URLs
wsgi.py
team/ # Django application "Team"
__init__.py
admin.py # Admin interface settings
apps.py
migrations/ # Database migrations
__init__.py
models.py # Model definitions (Footballer, Match, Tournament)
views.py # View class definitions
urls.py # App URLs
templates/ # HTML templates for views
base.html # Base template
footballer_list.html # Template for all footballers (Use ListView, GET)
footballer_update.html # Tempalate for updated personal info (Use UpdateView, UPDATE)
footballer_delete.html # Confirming deleted footballer (Use DeleteView, DELETE)
toggle_status.html # Template to check player status
Our application will use CRUD operations such as Update and Delete. Files and functions are named in such a way that you will understand which operation they refer to.
List of Objects
ListView is a view class that Django offers, which is useful when you want to display a list of model objects in the form of an HTML page. This view class is commonly used for making pages that display lists of items from the database. Examples can include blog posts, articles, and various store items. For example, in our "Football" project, we'll be using ListView to display a list of all soccer players.
Now, let's say you want to display a list of all the players stored in our database. For this, all we need to do is define the FootballerListView in the views.py file for the "Team" app.
from django.views.generic import ListView
from .models import Footballer
class FootballerListView(ListView):
model = Footballer
template_name = 'templates/footballer_list.html'
context_object_name = 'footballers'The model Footballer that the view will work with is defined using the model attribute. The template_name attribute indicates the HTML template file used to render the view. In our case, it's 'templates/footballer_list.html'. Your template might look something like this:
{% extends "base.html" %}
{% block content %}
<h2>List of footballers</h2>
<ul>
{% for footballer in footballers %}
<li>{{ footballer.name }}: {{ footballer.position }}</li>
{% empty %}
<li>No footballers available.</li>
{% endfor %}
</ul>
{% endblock %}The context_object_name attribute sets the name under which the list of footballers will be available in the template. If the context_object_name is not specified in the ListView, Django will automatically create a name for the context variable based on the name of the model to which the ListView is bound. This name will be generated by converting the model name to lowercase and adding "_list" at the end. Additionally, Django always adds a default context variable called 'object_list' to the template, which contains the list of objects. For example, if you have a ListView that works with the Product model and you have not specified a context_object_name, the default context variable for the list of objects will be named 'product_list', and 'object_list' will also be available in the template for the same list of objects.
When you open FootballerListView in Django, it uses the footballer_list.html template to display a list of footballers on the webpage. However, you also need to configure the URL route for the FootballerListView view. To do this, you need to add some code in the urls.py file for the "Team" application (team/urls.py), :
from django.urls import path
from .views import FootballerListView
urlpatterns = [
path('footballers/', FootballerListView.as_view(), name='footballer-list'),
]The get_queryset method also comes handy when you want to fine-tune the source data that the ListView should display. This method lets you define a restricted set of data that should be provided in the context of the ListView. As an example, if you have a Footballer model and want to display only the active footballers, you can override get_queryset like so:
from django.views.generic import ListView
from .models import Footballer
class ActiveFootballerListView(ListView):
model = Footballer
template_name = 'templates/footballer_list.html'
def get_queryset(self):
# return only active footballers
return Footballer.objects.filter(is_active=True)Now that you've learnt how to retrieve objects, let's move on to learn how we can use the built-in views to modify or delete existing objects of a given model.
UpdateView and DeleteView
In our "Football" project, we use two handy tools, UpdateView and DeleteView, to modify and erase data in our models. UpdateView simplifies editing existing model objects, whereas DeleteView lets you delete them. Let's try updating and deleting.
from django.views.generic.edit import UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Footballer
class FootballerUpdateView(UpdateView):
model = Footballer
template_name = 'templates/footballer_update.html'
fields = ['name', 'position', 'birthdate', 'nationality']
success_url = reverse_lazy('footballer-list')
class FootballerDeleteView(DeleteView):
model = Footballer
template_name = 'templates/footballer_delete.html'
success_url = reverse_lazy('footballer-list')We created a FootballerUpdateView which specifies the model (Footballer), the template used for editing ('templates/footballer_update.html'), the editable fields, and the post-successful-edit redirection URL ('footballer-list'). Similarly, we established a FootballerDeleteView that outlines the model (Footballer), the deletion confirmation template ('templates/footballer_delete.html'), and the post-deletion redirection URL ('footballer-list').
Here's what our template, footballer_update.html, will look like:
<!DOCTYPE html>
<html>
<head>
<title>Edit footballer</title>
</head>
<body>
<h2>Edit footballer</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Save changes</button>
</form>
<a href="{% url 'footballer-list' %}">Back to footballer list</a>
</body>
</html>And the deletion confirmation template, footballer_delete.html, will look like:
<!DOCTYPE html>
<html>
<head>
<title>Confirm deletion</title>
</head>
<body>
<h2>Confirm deletion</h2>
<p>Are you sure you want to delete this footballer?</p>
<form method="post">
{% csrf_token %}
<button type="submit">Confirm delete</button>
</form>
<a href="{% url 'footballer-list' %}">Cancel and go back</a>
</body>
</html>In the templates above, you'll notice we used form. Django forms boost user interaction in web templates. They facilitate data input and processing, such as user sign-up and login, in a user-friendly and secure way. By using 'forms' from 'django.forms', you can create intuitive, dependable forms.
To make your views (FootballerUpdateView and FootballerDeleteView) work, you must set up matching URL patterns for them in your urls.py file. Here's how to do that:
from django.urls import path
from .views import FootballerListView, FootballerUpdateView, FootballerDeleteView
urlpatterns = [
path('footballer/<int:pk>/edit/', FootballerUpdateView.as_view(), name='footballer-edit'),
path('footballer/<int:pk>/delete/', FootballerDeleteView.as_view(), name='footballer-delete'),
path('footballers/', FootballerListView.as_view(), name='footballer-list'),
]In the code above, we set up two routes (footballer-edit and footballer-delete) that correlate to the FootballerUpdateView and FootballerDeleteView views. We used the <int:pk> parameter in the URL to pass the footballer's identifier to the views, determining which footballer to edit or delete.
Once you establish these routes, you can edit a footballer through a URL like /footballer/1/edit/ (where 1 is the footballer's identifier) and confirm a deletion through a URL like /footballer/1/delete/.
Custom Modifying Views
Django allows you to work with views that make specific data modifications in your web application. These go further than simple CRUD operations; you can define unique actions based on user interactions, such as form submissions or button clicks. Custom views are handy for implementing complex data changes, batch operations, workflow triggers or specialized tasks. They provide flexibility, security, and user-friendly interfaces, allowing the tailoring of data modifications to fit your application's specific needs.
For instance, imagine you have a Footballer model and you want to introduce a function that changes the status of a footballer from "Active" to "Inactive" and vice versa. You use the "Change status" button on the web page for this operation. To set this up, add the URL route for the custom status change action in the urls.py file:
from django.urls import path
from .views import FootballerListView, FootballerUpdateView, FootballerDeleteView
urlpatterns = [
path('footballer/<int:pk>/toggle-status/', views.toggle_footballer_status, name='toggle-footballer-status'),
path('footballer/<int:pk>/edit/', FootballerUpdateView.as_view(), name='footballer-edit'),
path('footballer/<int:pk>/delete/', FootballerDeleteView.as_view(), name='footballer-delete'),
path('footballers/', FootballerListView.as_view(), name='footballer-list'),
]Next, create a view to change a player's status. You will use a functional view for this:
from django.shortcuts import render, redirect
from .models import Footballer
def toggle_footballer_status(request, pk):
footballer = Footballer.objects.get(pk=pk)
if request.method == 'POST':
footballer.status = not footballer.status
footballer.save()
return redirect('footballer-list')
return render(request, 'team/toggle_status.html', {'footballer': footballer})Create a 'toggle_status.html' template to show the "Change status" button:
<!DOCTYPE html>
<html>
<head>
<title>Toggle footballer status</title>
</head>
<body>
<h2>Toggle footballer status</h2>
<p>Current status: {{ footballer.status }}</p>
<form method="post">
{% csrf_token %}
<button type="submit">Toggle status</button>
</form>
<a href="{% url 'footballer-list' %}">Back to footballer list</a>
</body>
</html>This template shows the current status of a player and the "Change status" button. In your webpage listing the footballers, you can add a button to trigger a custom action:
<a href="{% url 'toggle-footballer-status' footballer.id %}">Toggle status</a>You can also adapt this example to use basic generic views, providing a consistent manner of developing views for objects. Even though it may involve more typing, it aligns with the development of other views and makes it easier to filter for basic elements.
GET/POST views
GET/POST views are necessary for the execution of basic interactions within web applications, such as displaying pages and handling form submissions. Django shows GET views to fetch and present information to the user. These views generally render templates and transfer data for rendering. For instance, when a user accesses a webpage, a GET view is in charge of rendering the content they see. Here's a simplified example of a Django GET view:
from django.shortcuts import render
def product_detail(request, product_id):
product = Product.objects.get(pk=product_id)
return render(request, 'product_detail.html', {'product': product})Here is the class-based version of the above view using the generic View class:
from django.views import View
from django.shortcuts import render
from .models import Product
class ProductDetailView(View):
def get(self, request, product_id):
product = Product.objects.get(pk=product_id)
return render(request, 'product_detail.html', {'product': product})On the other hand, POST views come into play when users input data via forms or other methods. These views manage data provided by the user, carry out necessary validations, and may update the database or execute other actions based on the received data. For instance, when a user submits a registration form, a POST view processes the form data and creates a new user account.
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
# Redirect to a success page or perform other operations
return redirect('registration_success')
else:
form = UserCreationForm()
return render(request, 'registration/register.html', {'form': form})This view can also be represented using the generic View class:
from django.views import View
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
class RegisterView(View):
def get(self, request):
form = UserCreationForm()
return render(request, 'registration/register.html', {'form': form})
def post(self, request):
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
# Redirect to a success page or perform other operations
return redirect('registration_success')
return render(request, 'registration/register.html', {'form': form})Let's take a look at the final version of the urls.py file:
from django.urls import path
from .views import (
FootballerListView,
FootballerUpdateView,
FootballerDeleteView,
toggle_footballer_status,
ProductDetailView,
RegisterView,
)
urlpatterns = [
path('footballers/', FootballerListView.as_view(), name='footballer-list'),
path('footballer/<int:pk>/edit/', FootballerUpdateView.as_view(), name='footballer-edit'),
path('footballer/<int:pk>/delete/', FootballerDeleteView.as_view(), name='footballer-delete'),
path('footballer/<int:pk>/toggle-status/', toggle_footballer_status, name='toggle-footballer-status'),
path('product/<int:product_id>/', ProductDetailView.as_view(), name='product-detail'),
path('register/', RegisterView.as_view(), name='register'),
]Utilizing built-in views, you have discovered an effective method to add CRUD (Create, Read, Update, Delete) operations to your models. While it might appear somewhat restricted, it efficaciously covers essential use cases, making it highly practical.
Conclusion
Django's class-based views provide an effective way to build web applications. By using views such as ListView, DeleteView, and UpdateView, you can streamline tasks like displaying item lists, deleting data, and updating information. When you add new views, remember the procedure and ensure that templates and URLs are correctly integrated.
ListView: This view lets you conveniently display lists of items in your app.
DeleteView: This view aids in erasing data, making it simpler to eliminate objects from the database.
UpdateView: This view eases the task of updating data, letting you alter existing objects in your app.
Custom Modifying Views: If you need to carry out operations beyond standard create, read, update, and delete, consider designing custom views to cater to specific demands.
GET/POST views: These views are helpful for basic interactions such as rendering pages and handling form submissions.
By adopting these concepts and utilizing Django's class-based views, you can develop robust and user-friendly web applications that cater to your users' needs.