3 =====================================================
4 django-tables - An app for creating HTML tables
5 =====================================================
7 django-tables simplifies the task of turning sets of datainto HTML tables. It
8 has native support for pagination and sorting. It does for HTML tables what
9 ``django.forms`` does for HTML forms.
14 1. Download and install the package.
15 2. Install the tables framework by adding ``'django_tables'`` to your
16 ``INSTALLED_APPS`` setting.
17 3. Ensure that ``'django.core.context_processors.request'`` is in your
18 ``TEMPLATE_CONTEXT_PROCESSORS`` setting.
19 4. Write table classes for the types of tables you want to display.
20 5. Create an instance of a table in a view, provide it your data, and pass it
21 to a template for display.
22 6. Use ``{{ table.as_html }}``, the
23 :ref:`template tag <template_tags.render_table>`, or your own
24 custom template code to display the table.
30 For each type of table you want to display, you will need to create a subclass
31 of ``django_tables.Table`` that describes the structure of the table.
33 In this example we are going to take some data describing three countries and
34 turn it into a HTML table. We start by creating our data:
36 .. code-block:: python
39 ... {'name': 'Australia', 'population': 21, 'tz': 'UTC +10', 'visits': 1},
40 ... {'name': 'Germany', 'population', 81, 'tz': 'UTC +1', 'visits': 2},
41 ... {'name': 'Mexico', 'population': 107, 'tz': 'UTC -6', 'visits': 0},
44 Next we subclass ``django_tables.Table`` to create a table that describes our
45 data. The API should look very familiar since it's based on Django's
48 .. code-block:: python
50 >>> import django_tables as tables
51 >>> class CountryTable(tables.Table):
52 ... name = tables.Column()
53 ... population = tables.Column()
54 ... tz = tables.Column(verbose_name='Time Zone')
55 ... visits = tables.Column()
61 To use the table, simply create an instance of the table class and pass in your
62 data. e.g. following on from the above example:
64 .. code-block:: python
66 >>> table = CountryTable(countries)
68 Tables have support for any iterable data that contains objects with
69 attributes that can be accessed as property or dictionary syntax:
71 .. code-block:: python
73 >>> table = SomeTable([{'a': 1, 'b': 2}, {'a': 4, 'b': 8}]) # valid
74 >>> table = SomeTable(SomeModel.objects.all()) # also valid
76 Each item in the data corresponds to one row in the table. By default, the
77 table uses column names as the keys (or attributes) for extracting cell values
78 from the data. This can be changed by using the :attr:`~Column.accessor`
85 There are two ways to display a table, the easiest way is to use the table's
86 own ``as_html`` method:
88 .. code-block:: django
92 Which will render something like:
94 +--------------+------------+---------+
95 | Country Name | Population | Tz |
96 +==============+============+=========+
97 | Australia | 21 | UTC +10 |
98 +--------------+------------+---------+
99 | Germany | 81 | UTC +1 |
100 +--------------+------------+---------+
101 | Mexico | 107 | UTC -6 |
102 +--------------+------------+---------+
104 The downside of this approach is that pagination and sorting will not be
105 available. These features require the use of the ``{% render_table %}``
108 .. code-block:: django
110 {% load django_tables %}
111 {% render_table table %}
113 See :ref:`template_tags` for more information.
119 Controlling the order that the rows are displayed (sorting) is simple, just use
120 the :attr:`~Table.order_by` property or pass it in when initialising the
123 .. code-block:: python
125 >>> # order_by argument when creating table instances
126 >>> table = CountryTable(countries, order_by='name, -population')
127 >>> table = CountryTable(countries, order_by=('name', '-population'))
128 >>> # order_by property on table instances
129 >>> table = CountryTable(countries)
130 >>> table.order_by = 'name, -population'
131 >>> table.order_by = ('name', '-population')
134 Customising the output
135 ======================
137 There are a number of options available for changing the way the table is
138 rendered. Each approach provides balance of ease-of-use and control (the more
139 control you want, the less easy it is to use).
144 If you want to affect the appearance of the table using CSS, you probably want
145 to add a ``class`` or ``id`` attribute to the ``<table>`` element. This can be
146 achieved by specifying an ``attrs`` variable in the table's ``Meta`` class.
148 .. code-block:: python
150 >>> import django_tables as tables
151 >>> class SimpleTable(tables.Table):
152 ... id = tables.Column()
153 ... age = tables.Column()
156 ... attrs = {'class': 'mytable'}
158 >>> table = SimpleTable()
160 '<table class="mytable">...'
162 The :attr:`Table.attrs` property actually returns an :class:`AttributeDict`
163 object. These objects are identical to :class:`dict`, but have an
164 :meth:`AttributeDict.as_html` method that returns a HTML tag attribute string.
166 .. code-block:: python
168 >>> from django_tables.utils import AttributeDict
169 >>> attrs = AttributeDict({'class': 'mytable', 'id': 'someid'})
171 'class="mytable" id="someid"'
173 The returned string is marked safe, so it can be used safely in a template.
178 Using a formatter is a quick way to adjust the way values are displayed in a
179 column. A limitation of this approach is that you *only* have access to a
180 single attribute of the data source.
182 To use a formatter, simply provide the :attr:`~Column.formatter` argument to a
183 :class:`Column` when you define the :class:`Table`:
185 .. code-block:: python
187 >>> import django_tables as tables
188 >>> class SimpleTable(tables.Table):
189 ... id = tables.Column(formatter=lambda x: '#%d' % x)
190 ... age = tables.Column(formatter=lambda x: '%d years old' % x)
192 >>> table = SimpleTable([{'age': 31, 'id': 10}, {'age': 34, 'id': 11}])
193 >>> row = table.rows[0]
200 As you can see, the only the value of the column is available to the formatter.
201 This means that **it's impossible create a formatter that incorporates other
202 values of the record**, e.g. a column with an ``<a href="...">`` that uses
203 :func:`reverse` with the record's ``pk``.
205 If formatters aren't powerful enough, you'll need to either :ref:`create a
206 Column subclass <subclassing-column>`, or to use the
207 :ref:`Table.render_FOO method <table.render_foo>`.
210 .. _table.render_foo:
212 :meth:`Table.render_FOO` Method
213 -------------------------------
215 This approach provides a lot of control, but is only suitable if you intend to
216 customise the rendering for a single table (otherwise you'll end up having to
217 copy & paste the method to every table you want to modify – which violates
220 The example below has a number of different techniques in use:
222 * :meth:`Column.render` (accessible via :attr:`BoundColumn.column`) applies the
223 *formatter* if it's been provided. The effect of this behaviour can be seen
224 below in the output for the ``id`` column. Square brackets (from the
225 *formatter*) have been applied *after* the angled brackets (from the
226 :meth:`~Table.render_FOO`).
227 * Completely abitrary values can be returned by :meth:`render_FOO` methods, as
228 shown in :meth:`~SimpleTable.render_row_number` (a :attr:`_counter` attribute
229 is added to the :class:`SimpleTable` object to keep track of the row number).
231 This is possible because :meth:`render_FOO` methods override the default
232 behaviour of retrieving a value from the data-source.
234 .. code-block:: python
236 >>> import django_tables as tables
237 >>> class SimpleTable(tables.Table):
238 ... row_number = tables.Column()
239 ... id = tables.Column(formatter=lambda x: '[%s]' % x)
240 ... age = tables.Column(formatter=lambda x: '%d years old' % x)
242 ... def render_row_number(self, bound_column, bound_row):
243 ... value = getattr(self, '_counter', 0)
244 ... self._counter = value + 1
245 ... return 'Row %d' % value
247 ... def render_id(self, bound_column, bound_row):
248 ... value = bound_column.column.render(table=self,
249 ... bound_column=bound_column,
250 ... bound_row=bound_row)
251 ... return '<%s>' % value
253 >>> table = SimpleTable([{'age': 31, 'id': 10}, {'age': 34, 'id': 11}])
254 >>> for cell in table.rows[0]:
261 The :meth:`Column.render` method is what actually performs the lookup into a
262 record to retrieve the column value. In the example above, the
263 :meth:`render_row_number` never called :meth:`Column.render` and as a result
264 there was not attempt to access the data source to retrieve a value.
270 And of course if you want full control over the way the table is rendered,
271 ignore the built-in generation tools, and instead pass an instance of your
272 :class:`Table` subclass into your own template, and render it yourself:
274 .. code-block:: django
276 {% load django_tables %}
280 {% for column in table.columns %}
281 <th><a href="{% set_url_param sort=column.name_toggled %}">{{ column }}</a></th>
286 {% for row in table.rows %}
288 {% for cell in row %}
297 .. _subclassing-column:
299 Subclassing :class:`Column`
300 ---------------------------
302 If you want to have a column behave the same way in many tables, it's best to
303 create a subclass of :class:`Column` and use that when defining the table.
305 To change the way cells are rendered, simply override the
306 :meth:`~Column.render` method.
308 .. code-block:: python
310 >>> import django_tables as tables
312 >>> class AngryColumn(tables.Column):
313 ... def render(self, *args, **kwargs):
314 ... raw = super(AngryColumn, self).render(*args, **kwargs)
315 ... return raw.upper()
317 >>> class Example(tables.Table):
318 ... normal = tables.Column()
319 ... angry = AngryColumn()
322 ... 'normal': 'May I have some food?',
323 ... 'angry': 'Give me the food now!',
325 ... 'normal': 'Hello!',
326 ... 'angry': 'What are you looking at?',
329 >>> table = Example(data)
331 u'<table><thead><tr><th>Normal</th><th>Angry</th></tr></thead><tbody><tr><td>May I have some food?</td><td>GIVE ME THE FOOD NOW!</td></tr><tr><td>Hello!</td><td>WHAT ARE YOU LOOKING AT?</td></tr></tbody></table>\n'
333 Which, when displayed in a browser, would look something like this:
335 +-----------------------+--------------------------+
337 +=======================+==========================+
338 | May I have some food? | GIVE ME THE FOOD NOW! |
339 +-----------------------+--------------------------+
340 | Hello! | WHAT ARE YOU LOOKING AT? |
341 +-----------------------+--------------------------+
344 If you plan on returning HTML from a :meth:`~Column.render` method, you must
345 remember to mark it as safe (otherwise it will be escaped when the table is
346 rendered). This can be achieved by using the :func:`mark_safe` function.
348 .. code-block:: python
350 >>> from django.utils.safestring import mark_safe
352 >>> class ImageColumn(tables.Column):
353 ... def render(self, **kwargs):
354 ... raw = super(AngryColumn, self).render(**kwargs)
355 ... return mark_safe('<img src="/media/img/%s.jpg" />' % raw)
365 .. _template_tags.render_table:
370 If you want to render a table that provides support for sorting and pagination,
371 you must use the ``{% render_table %}`` template tag. In this example ``table``
372 is an instance of a :class:`django_tables.Table` that has been put into the
375 .. code-block:: django
377 {% load django_tables %}
378 {% render_table table %}
381 .. _template_tags.set_url_param:
386 This template tag is a utility that allows you to update a portion of the
387 query-string without overwriting the entire thing. However you shouldn't need
388 to use this template tag unless you are rendering the table from scratch (i.e.
389 not using ``as_html()`` or ``{% render_table %}``).
391 This is very useful if you want the give your users the ability to interact
392 with your table (e.g. change the ordering), because you will need to create
393 urls with the appropriate queries.
395 Let's assume we have the query-string
396 ``?search=pirates&sort=name&page=5`` and we want to update the ``sort``
399 .. code-block:: django
401 {% set_url_param sort="dob" %} # ?search=pirates&sort=dob&page=5
402 {% set_url_param sort="" %} # ?search=pirates&page=5
403 {% set_url_param sort="" search="" %} # ?page=5
407 A table instance bound to data has two attributes ``columns`` and ``rows``,
408 which can be iterated over:
410 .. code-block:: django
415 {% for column in table.columns %}
416 <th><a href="?sort={{ column.name_toggled }}">{{ column }}</a></th>
421 {% for row in table.rows %}
423 {% for value in row %}
435 :class:`Table` Objects:
436 ------------------------
438 .. autoclass:: django_tables.tables.Table
442 :class:`TableOptions` Objects:
443 ------------------------------
445 .. autoclass:: django_tables.tables.TableOptions
449 :class:`Column` Objects:
450 ------------------------
452 .. autoclass:: django_tables.columns.Column
453 :members: __init__, default, render
456 :class:`Columns` Objects
457 ------------------------
459 .. autoclass:: django_tables.columns.Columns
460 :members: __init__, all, items, names, sortable, visible, __iter__,
461 __contains__, __len__, __getitem__
464 :class:`BoundColumn` Objects
465 ----------------------------
467 .. autoclass:: django_tables.columns.BoundColumn
468 :members: __init__, table, column, name, accessor, default, formatter,
469 sortable, verbose_name, visible
472 :class:`Rows` Objects
473 ---------------------
475 .. autoclass:: django_tables.rows.Rows
476 :members: __init__, all, page, __iter__, __len__, count, __getitem__
479 :class:`BoundRow` Objects
480 -------------------------
482 .. autoclass:: django_tables.rows.BoundRow
483 :members: __init__, __getitem__, __contains__, __iter__, record, table
486 :class:`AttributeDict` Objects
487 ------------------------------
489 .. autoclass:: django_tables.utils.AttributeDict
493 :class:`OrderBy` Objects
494 ------------------------
496 .. autoclass:: django_tables.utils.OrderBy
500 :class:`OrderByTuple` Objects
501 -----------------------------
503 .. autoclass:: django_tables.utils.OrderByTuple
504 :members: __contains__, __getitem__, __unicode__
513 The traditional concept of a table. i.e. a grid of rows and columns