Photo by Abel Y Costa on Unsplash

Django tricks: Quickly create a table view

Adrien Van Thong

--

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!

--

--