Django-tables2 is a great package for easily adding useful table features, like  sorting and pagination.  Having large numbers represented in a  table is something I assume is a fairly frequent occurrence, so it is a  bit disappointing that such an obvious thing isn’t covered by any of the documentation. So lets get started.First we need to install django-tables2.

pip install django-tables2

Next add it to your INSTALLED_APPS ('django_tables2',) in the settings.py file.For this example I am using a simple app for purchase orders. Here are my models.

class PurchaseOrder(models.Model):
    po_number = models.IntegerField()
    slug = models.SlugField(unique = True)

    def __str__(self):
        return self.slug

class LineItems(models.Model):
    po = models.ForeignKey('PurchaseOrder')
    product = models.CharField(max_length=100)
    quantity = models.IntegerField()
    price = models.DecimalField(max_digits=8, decimal_places=2)
    slug = models.SlugField(unique = True)

    def _get_total(self): #"Calculated" total field
        return (self.price * self.quantity) # returns the total
    total = property(_get_total)

    def __str__(self):
        return self.slug

So following along with the documentation found on readthedocs lets setup a tables.py file.

import django_tables2 as tables

class LineItemsTable(tables.Table):
    product = tables.Column()
    quantity = tables.Column()
    price = tables.Column()
    total = tables.Column()

    class Meta:
        attrs = {'class': 'table'}

Since I don’t want every field to show in the table (the slug and id  fields have no business in this table) I am only listing the ones I want to show rather than just using the model variable in the Meta class (as shown in readthedocs). I did however use the Meta class to set the CSS  style using the attrs variable.OK now on views.py.

from django.shortcuts import render
from django.views.generic import View
from .models import PurchaseOrder, LineItems

from django_tables2   import RequestConfig #5
from .tables  import LineItemsTable #6


class PO(View):
    def get(self, request, purchaseorder):
        po = PurchaseOrder.objects.get(po_number=purchaseorder)
        lineitems = LineItems.objects.filter(po__slug=po)
        table = LineItemsTable(lineitems)#13
        grandtotal = 0

        for lineitem in lineitems: 
            grandtotal += lineitem.total

        RequestConfig(request, paginate=False).configure(table)#19

        context = {'po': po, 'grandtotal': grandtotal, 'table': table}
        return render(request, 'po.html', context)

So lines 5,6,13, and 19 (annotated with comments) are all the lines  related to the table.  So far so good. Everything I have done so  far can been figured out quite easily from the info on readthedocs. In  line 19 you can see I took out the pagination. Since this is just an  example (and I only have a few records) pagination didn’t make much  sense here.
So here is the code in my template, which I am using to display the table.

{% load render_table from django_tables2 %}
<div class="col-md-6">
    {% render_table table %}
    <h4 align="right">Grand Total: {{ grandtotal }}</h4>
</div>

So lets take a look at our table.

Well hey, that is pretty neat! If you have actually been following along you will notice that you can click the column headings to sort (note  the table above in this blog is just a screenshot, so you can’t sort on  it). This a good start, but it would be way better if we could have a thousands separator.  Maybe the custom rendering section will tell us how to add a thousands separator? Nope, out of luck. Hmm,…well I know in a template I can use intcomma from django.contrib.humanize to add comma separators to numbers. But I normally use that in the template like this:

{% load humanize %}
{{ variablename|intcomma }}

But we are just using {% render_table table %} in our template and we can’t pass a filter to it like we can on a normal variable.  So, I wonder if there is way to load humanize outside of the template. Turns  out you can just import django.contrib.humanize.templatetags.humanize  into any of your .py files! Lets import this into our tables.py and make a class for columns with a thousands separator.

import django_tables2 as tables
from django.contrib.humanize.templatetags.humanize import intcomma

class ColumnWithThousandsSeparator(tables.Column):
    def render(self,value):
        return intcomma(value)

class LineItemsTable(tables.Table):
    product = tables.Column()
    quantity = ColumnWithThousandsSeparator()
    price = ColumnWithThousandsSeparator()
    total = ColumnWithThousandsSeparator()

    class Meta:
        attrs = {'class': 'table'}

Now lets take a look at our table.

Now we are getting somewhere. But there are still two things I think  need to be done. First we need to add the intcomma filter to the grand  total. Since the grand total isn’t part of the actual table we will do  this in the template (don’t forget to add django.contrib.humanize to  your INSTALLED_APPS in your settings.py file!) .

{% load render_table from django_tables2 %}
{% load humanize %}
<div class="col-md-6">
    {% render_table table %}
    <h4 align="right">Grand Total: ${{ grandtotal|intcomma }}</h4>
</div>

You might have noticed that I also added a dollar sign in front of  the grand total, which brings me to the next thing, dollar signs. Lets  add another class in tables.py for a currency column.

import django_tables2 as tables
from django.contrib.humanize.templatetags.humanize import intcomma

class ColumnWithThousandsSeparator(tables.Column):
    def render(self,value):
        return intcomma(value)

class CurrencyColumn(tables.Column):
    def render(self,value):
        return "$" + str(intcomma(value))

class LineItemsTable(tables.Table):
    product = tables.Column()
    quantity = ColumnWithThousandsSeparator()
    price = CurrencyColumn()
    total = CurrencyColumn()

    class Meta:
        attrs = {'class': 'table'}

OK, we added the CurrencyColumn class and changed the price and total to use it. Lets take a look at the final result.