Django tricks: Quickly create a table view
Django CBVs are incredible for flexibility and ease of use, and the Django community is burgeoning with addons and plugins that add even more functionality on top of that.
In this article we’ll go over the django-tables plugin, which make it a snap to organize a list of records from your CBV into a neat table, with sortable columns, pagination, and more.
For the purposes of this article we’ll start with this simple Model and View, and we’ll apply the django-table plugin functionality on top of it:
from django.db import models
from django.views.generic import ListView
class Movie(models.Model):
RATINGS = [
(1, "Trash"),
(2, "C-tier"),
(3, "B-tier"),
(4, "A-tier"),
(5, "S-tier"),
]
title = models.CharField(max_length=100)
summary = models.TextField()
score = models.IntegerField(choices=RATINGS)
genre = models.models.ForeignKey(Genre, on_delete=models.CASCADE, null=True)
class MovieListView(ListView):
model = Movie
I’ll assume that your environment already has the plugin installed. Directions to install this plugin can be found on the django-tables plugin docs.
First, create a Table class
The first step in making a nice table for our view above is to define what the columns will be and how that data will be displayed. To do this we implement the plugin’s Table
class, as seen below:
from django_tables2 import Table, Column
class MovieTable(Table):
title = Column(linkify=True)
class Meta:
model = Movie
fields = ("title", "score", "genre")
orderable = True
Setting the linkify=True
attribute on the title
column will make it so values generated in that column are automatically hyperlinked to that record’s get_absolute_url()
value. Omit this line to turn that behaviour off.
Next, update the CBV
Once our table is created, the changes to the view are trivial: all we need to do is inherit from the SingleTableView
and set a table_class
property, seen below:
from django_tables2 import SingleTableView
from my_app.tables import MovieTable
class MovieListView(SingleTableView):
model = Movie
table_class = MovieTable
paginate_by = 15
Finally, update the template
With the view class updated, the only step left is to update the template code to leverage the django-table framework. The great news here is that it’s a single line of code to generate both the table and corresponding pagination elements in one go.
{% load django_tables2 %}
<h1>All movies</h1>
{% render_table table %}
The plugin automatically assigns the contents of the table to the table
variable which is then passed in to the render_table
templatetag. This helps drastically reduce the amount of template code we have to write!
Customizing the columns
The example above covers the simple use case where each Model field gets its own column, but what if we wanted to spice it up with some extra columns?
The django-tables plugin also allows for some easy customizations as well. For example let’s say we wanted to add some extra columns with an “update” and “delete” buttons, here is what that might look like:
from django_tables2 import Table, Column, TemplateColumn
class MovieTable(Table):
title = Column(linkify=True)
edit = TemplateColumn(template_code="<a href='{% url 'edit_movie' record.pk %}'>Edit</a>", orderable=False)
delete= TemplateColumn(template_code="<a href='{% url 'delete_movie' record.pk %}'>Delete</a>", orderable=False)
class Meta:
model = Movie
fields = ("title", "score", "genre", "edit", "delete")
orderable = True
In order to leverage template code inside the column value, we use the TemplateColumn
class for these columns, and we set the orderable=False
attribute to tell the plugin these columns aren’t sortable.
What about filtering?
As noted earlier, django-tables automatically handles pagination for us, should we need it. However, no web table with sortable columns is complete without providing users some nifty filters! Another neat feature of Django-tables is its seamless integration with django-filters.
I describe how to easily integrate django-filters to an existing CBV in a previous article — the steps to integrate a CBV with both django-tables and django-filters are exactly the same.
A sample FilterSet
class for our Movie
example may look as such:
from django_filters.filterset import FilterSet
from .models import Movie
class MovieFilter(FilterSet):
class Meta:
model = Movie
fields = ["title", "score", "genre"]
With our Filter
class ready to go, we integrate it to our table CBV as such:
from django_tables2 import SingleTableView
from django_filters.views import FilterView
from my_app.tables import MovieTable
from my_app.filters import MovieFilter
class MovieListView(FilterView, SingleTableView):
model = Movie
table_class = MovieTable
filterset_class = MovieFilter
paginate_by = 15
Finally, we add the filters to our template:
{% load django_tables2 %}
<h1>Filters</h1>
<form method="get" action="">
{{ filter.form }}
<input type="submit" value="Filter" />
</form>
<h1>All movies</h1>
{% render_table table %}
Recommended Reading
It’s that simple! Django and its plethora of amazing plugins once again make our jobs as app developers extremely easy. If you’ve made it this far, please don’t forget to show support for these incredible plugin creators linked above!