Alternative column data¶
Various options are available for changing the way the table is rendered. Each approach has a different balance of ease-of-use and flexibility.
Using Accessors
¶
Each column has a ‘key’ that describes which value to pull from each record to populate the column’s cells. By default, this key is just the name given to the column, but it can be changed to allow foreign key traversal or other complex cases.
To reduce ambiguity, rather than calling it a ‘key’, we use the name ‘accessor’.
Accessors are just double-underscore separated paths that describe how an object should be traversed to reach a specific value, for example:
>>> from django_tables2 import A
>>> data = {"abc": {"one": {"two": "three"}}}
>>> A("abc__one__two").resolve(data)
'three'
The separators __
represent relationships, and are attempted in this order:
Dictionary lookup
a[b]
Attribute lookup
a.b
List index lookup
a[int(b)]
If the resulting value is callable, it is called and the return value is used.
Table.render_foo
methods¶
To change how a column is rendered, define a render_foo
method on
the table for example: render_row_number()
for a column named row_number
.
This approach is suitable if you have a one-off change that you do not want to
use in multiple tables or if you want to combine the data from two columns into one.
Supported keyword arguments include:
record
– the entire record for the row from the table datavalue
– the value for the cell retrieved from the table datacolumn
– theColumn
objectbound_column
– theBoundColumn
objectbound_row
– theBoundRow
objecttable
– alias forself
This example shows how to render the row number in the first row:
>>> import django_tables2 as tables
>>> import itertools
>>>
>>> class SimpleTable(tables.Table):
... row_number = tables.Column(empty_values=())
... id = tables.Column()
... age = tables.Column()
...
... def __init__(self, *args, **kwargs):
... super().__init__(*args, **kwargs)
... self.counter = itertools.count()
...
... def render_row_number(self):
... return "Row %d" % next(self.counter)
...
... def render_id(self, value):
... return "<%s>" % value
...
>>> table = SimpleTable([{"age": 31, "id": 10}, {"age": 34, "id": 11}])
>>> print(", ".join(map(str, table.rows[0])))
Row 0, <10>, 31
Python’s inspect.getargspec
is used to only pass the arguments declared by the
function. This means it’s not necessary to add a catch all (**
) keyword
argument.
The render_foo
method can also be used to combine data from two columns into one column.
The following example shows how the the value for the last_name
field is appended to the
name
field using the render_name
function.
Note that value
is the value in the column and record
is used to access the values in
the last_name
column:
# models.py
class Customers(models.Model):
name = models.CharField(max_length=50, null=False, blank=False)
last_name = models.CharField(max_length=50, null=False, blank=False)
description = models.TextField(blank=True)
# tables.py
from .models import Customers
from django.utils.html import format_html
class CustomerTable(tables.Table):
name = tables.Column()
description = tables.Column()
def render_name(self, value, record):
return format_html("<b>{} {}</b>", value, record.last_name)
If you need to access logged-in user (or request in general) in your render methods, you can reach it through
self.request
:
def render_count(self, value):
if self.request.user.is_authenticated():
return value
else:
return '---'
Important
render_foo
methods are only called if the value for a cell is determined to
be not an empty value. When a value is in Column.empty_values
,
a default value is rendered instead (both Column.render
and
Table.render_FOO
are skipped).
Important
render_foo
methods determine what value is rendered, but which make sorting the
column have unexpected results. In those cases, you might want to also define a
table.order_FOO() methods method.
Table.value_foo
methods¶
If you want to use Table.as_values
to export your data, you might want to define
a method value_foo
, which is analogous to render_foo
, but used to render the
values rather than the HTML output.
Please refer to Table.as_values
for an example.
Subclassing Column
¶
Defining a column subclass allows functionality to be reused across tables.
Columns have a render
method that behaves the same as Table.render_foo methods
methods on tables:
>>> import django_tables2 as tables
>>>
>>> class UpperColumn(tables.Column):
... def render(self, value):
... return value.upper()
...
>>> class Example(tables.Table):
... normal = tables.Column()
... upper = UpperColumn()
...
>>> data = [{"normal": "Hi there!",
... "upper": "Hi there!"}]
...
>>> table = Example(data)
>>> # renders to something like this:
'''<table>
<thead><tr><th>Normal</th><th>Upper</th></tr></thead>
<tbody><tr><td>Hi there!</td><td>HI THERE!</td></tr></tbody>
</table>'''
See Table.render_foo methods for a list of arguments that can be accepted.
For complicated columns, you may want to return HTML from the
render()
method. Make sure to use Django’s html formatting functions:
>>> from django.utils.html import format_html
>>>
>>> class ImageColumn(tables.Column):
... def render(self, value):
... return format_html('<img src="/media/img/{}.jpg" />', value)
...