class Column(object):
"""Represents a single column of a table.
- ``verbose_name`` defines a display name for this column used for output.
+ :class:`Column` objects control the way a column (including the cells that
+ fall within it) are rendered.
- You can use ``visible`` to flag the column as hidden by default.
- However, this can be overridden by the ``visibility`` argument to the
- table constructor. If you want to make the column completely unavailable
- to the user, set ``inaccessible`` to True.
-
- Setting ``sortable`` to False will result in this column being unusable
- in ordering. You can further change the *default* sort direction to
- descending using ``direction``. Note that this option changes the actual
- direction only indirectly. Normal und reverse order, the terms
- django-tables exposes, now simply mean different things.
-
- Data can be formatted by using ``formatter``, which accepts a callable as
- an argument (e.g. lambda x: x.upper())
"""
- # Tracks each time a Column instance is created. Used to retain order.
+ #: Tracks each time a Column instance is created. Used to retain order.
creation_counter = 0
def __init__(self, verbose_name=None, accessor=None, default=None,
visible=True, sortable=None, formatter=None):
+ """Initialise a :class:`Column` object.
+
+ :param verbose_name:
+ A pretty human readable version of the column name. Typically this
+ is used in the header cells in the HTML output.
+
+ :param accessor:
+ A string or callable that specifies the attribute to access when
+ retrieving the value for a cell in this column from the data-set.
+ Multiple lookups can be achieved by providing a dot separated list
+ of lookups, e.g. ``"user.first_name"``. The functionality is
+ identical to that of Django's template variable syntax, e.g. ``{{
+ user.first_name }}``
+
+ A callable should be used if the dot separated syntax is not
+ capable of describing the lookup properly. The callable will be
+ passed a single item from the data (if the table is using
+ :class:`QuerySet` data, this would be a :class:`Model` instance),
+ and is expected to return the correct value for the column.
+
+ Consider the following:
+
+ .. code-block:: python
+
+ >>> import django_tables as tables
+ >>> data = [
+ ... {'dot.separated.key': 1},
+ ... {'dot.separated.key': 2},
+ ... ]
+ ...
+ >>> class SlightlyComplexTable(tables.Table):
+ >>> dot_seperated_key = tables.Column(accessor=lambda x: x['dot.separated.key'])
+ ...
+ >>> table = SlightlyComplexTable(data)
+ >>> for row in table.rows:
+ >>> print row['dot_seperated_key']
+ ...
+ 1
+ 2
+
+ This would **not** have worked:
+
+ .. code-block:: python
+
+ dot_seperated_key = tables.Column(accessor='dot.separated.key')
+
+ :param default:
+ The default value for the column. This can be a value or a callable
+ object [1]_. If an object in the data provides :const:`None` for a
+ column, the default will be used instead.
+
+ The default value may affect ordering, depending on the type of
+ data the table is using. The only case where ordering is not
+ affected ing when a :class:`QuerySet` is used as the table data
+ (since sorting is performed by the database).
+
+ .. [1] The provided callable object must not expect to receive any
+ arguments.
+
+ :param visible:
+ If :const:`False`, this column will not be in HTML from output
+ generators (e.g. :meth:`as_html` or ``{% render_table %}``).
+
+ When a field is not visible, it is removed from the table's
+ :attr:`~Column.columns` iterable.
+
+ :param sortable:
+ If :const:`False`, this column will not be allowed to be used in
+ ordering the table.
+
+ :param formatter:
+ A callable object that is used as a final step in formatting the
+ value for a cell. The callable will be passed the string that would
+ have otherwise been displayed in the cell.
+
+ In the following table, cells in the *name* column have upper-case
+ values.
+
+ .. code-block:: python
+
+ class Example(tables.Table):
+ name = tables.Column(formatter=lambda x: x.upper())
+
+ """
if not (accessor is None or isinstance(accessor, basestring) or
callable(accessor)):
raise TypeError('accessor must be a string or callable, not %s' %
@property
def default(self):
- """Since ``Column.default`` property may be a callable, this function
- handles access.
+ """The default value for cells in this column.
+
+ The default value passed into ``Column.default`` property may be a
+ callable, this function handles access.
+
"""
return self._default() if callable(self._default) else self._default
def render(self, table, bound_column, bound_row):
"""Returns a cell's content.
This method can be overridden by ``render_FOO`` methods on the table or
- by subclassing ``Column``.
+ by subclassing :class:`Column`.
+
"""
return table.data.data_for_cell(bound_column=bound_column,
bound_row=bound_row)
class CheckBoxColumn(Column):
- """A subclass of Column that renders its column data as a checkbox
-
- ``name`` is the html name of the checkbox.
- """
+ """A subclass of Column that renders its column data as a checkbox"""
def __init__(self, attrs=None, *args, **kwargs):
+ """
+ :param attrs: a dict of HTML element attributes to be added to the
+ ``<input>``
+
+ """
super(CheckBoxColumn, self).__init__(*args, **kwargs)
self.attrs = attrs or {}
be possible to incorporate the row number into the cell's value.
-Column render method
---------------------
+:meth:`Table.render_FOO` Method
+-------------------------------
This approach provides a lot of control, but is only suitable if you intend to
customise the rendering for a single table (otherwise you'll end up having to
copy & paste the method to every table you want to modify – which violates
DRY).
+The example below has a number of different techniques in use:
+
+* :meth:`Column.render` (accessible via :attr:`BoundColumn.column`) applies the
+ *formatter* function if it's been provided. This is evident in the order that
+ the square and angled brackets have been applied for the ``id`` column.
+* Completely abitrary values can be returned by :meth:`render_FOO` methods, as
+ shown in :meth:`~SimpleTable.render_row_number` (a :attr:`_counter` attribute
+ is added to the :class:`SimpleTable` object to keep track of the row number).
+
+ This is possible because :meth:`render_FOO` methods override the default
+ behaviour of retrieving a value from the data-source.
+
+.. code-block:: python
+
>>> import django_tables as tables
>>> class SimpleTable(tables.Table):
... row_number = tables.Column()
- ... id = tables.Column(formatter=lambda x: '#%d' % x)
+ ... id = tables.Column(formatter=lambda x: '[%s]' % x)
... age = tables.Column(formatter=lambda x: '%d years old' % x)
...
... def render_row_number(self, bound_column, bound_row):
- ... value =
+ ... value = getattr(self, '_counter', 0)
+ ... self._counter = value + 1
+ ... return 'Row %d' % value
...
... def render_id(self, bound_column, bound_row):
- ... value = self.column.
+ ... value = bound_column.column.render(table=self,
+ ... bound_column=bound_column,
+ ... bound_row=bound_row)
+ ... return '<%s>' % value
...
>>> table = SimpleTable([{'age': 31, 'id': 10}, {'age': 34, 'id': 11}])
>>> for cell in table.rows[0]:
... print cell
...
- #10
+ Row 0
+ <[10]>
31 years old
-If you want full control over the way the table is rendered, create
-and render the template yourself:
+
+Custom Template
+---------------
+
+And of course if you want full control over the way the table is rendered,
+ignore the built-in generation tools, and instead pass an instance of your
+:class:`Table` subclass into your own template, and render it yourself:
.. code-block:: django
</table>
+Subclassing :class:`Column`
+---------------------------
-:class:`Columns` Objects
-========================
-
-.. autoclass:: django_tables.columns.Columns
- :members: __init__, all, items, names, sortable, visible, __iter__,
- __contains__, __len__, __getitem__
-
-
-:class:`BoundColumn` Objects
-============================
-
-.. autoclass:: django_tables.columns.BoundColumn
- :members: __init__, table, column, name, accessor, default, formatter,
- sortable, verbose_name, visible
-
-
-Column options
---------------
-
-Each column takes a certain set of column-specific arguments.
-
-There's also a set of common arguments available to all column types. All are
-optional. Here's a summary of them.
-
- :attr:`~Column.verbose_name`
- A pretty human readable version of the column name. Typically this is
- used in the header cells in the HTML output.
-
- :attr:`~Column.accessor`
- A string or callable that specifies the attribute to access when
- retrieving the value for a cell in this column from the data-set.
- Multiple lookups can be achieved by providing a dot separated list of
- lookups, e.g. ``"user.first_name"``. The functionality is identical to
- that of Django's template variable syntax, e.g. ``{{ user.first_name
- }}``
-
- A callable should be used if the dot separated syntax is not capable of
- describing the lookup properly. The callable will be passed a single
- item from the data (if the table is using :class:`QuerySet` data, this
- would be a :class:`Model` instance), and is expected to return the
- correct value for the column.
-
- Consider the following:
-
- .. code-block:: python
-
- >>> import django_tables as tables
- >>> data = [
- ... {'dot.separated.key': 1},
- ... {'dot.separated.key': 2},
- ... ]
- ...
- >>> class SlightlyComplexTable(tables.Table):
- >>> dot_seperated_key = tables.Column(accessor=lambda x: x['dot.separated.key'])
- ...
- >>> table = SlightlyComplexTable(data)
- >>> for row in table.rows:
- >>> print row['dot_seperated_key']
- ...
- 1
- 2
+If you want to have a column behave the same way in many tables, it's best to
+create a subclass of :class:`Column` and use that when defining the table.
- This would not have worked:
+To change the way cells are rendered, simply override the
+:meth:`~Column.render` method.
- .. code-block:: python
-
- dot_seperated_key = tables.Column(accessor='dot.separated.key')
-
- :attr:`~Column.default`
- The default value for the column. This can be a value or a callable
- object [1]_. If an object in the data provides :const:`None` for a
- column, the default will be used instead.
-
- The default value may affect ordering, depending on the type of
- data the table is using. The only case where ordering is not affected
- ing when a :class:`QuerySet` is used as the table data (since sorting
- is performed by the database).
-
- .. [1] The provided callable object must not expect to receive any
- arguments.
-
- :attr:`~Column.visible`
- If :const:`False`, this column will not be in the HTML output.
-
- When a field is not visible, it is removed from the table's
- :attr:`~Column.columns` iterable.
-
- :attr:`~Column.sortable`
- If :const:`False`, this column will not be allowed to be used in
- ordering the table.
+.. code-block:: python
- :attr:`~Column.formatter`
- A callable object that is used as a final step in formatting the value
- for a cell. The callable will be passed the string that would have
- otherwise been displayed in the cell.
+ >>> import django_tables as tables
+ >>>
+ >>> class AngryColumn(tables.Column):
+ ... def render(self, *args, **kwargs):
+ ... raw = super(AngryColumn, self).render(*args, **kwargs)
+ ... return raw.upper()
+ ...
+ >>> class Example(tables.Table):
+ ... normal = tables.Column()
+ ... angry = AngryColumn()
+ ...
+ >>> data = [{
+ ... 'normal': 'May I have some food?',
+ ... 'angry': 'Give me the food now!',
+ ... }, {
+ ... 'normal': 'Hello!',
+ ... 'angry': 'What are you looking at?',
+ ... }]
+ ...
+ >>> table = Example(data)
+ >>> table.as_html()
+ 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'
+Which, when displayed in a browser, would look something like this:
-Rows
-====
++-----------------------+--------------------------+
+| Normal | Angry |
++=======================+==========================+
+| May I have some food? | GIVE ME THE FOOD NOW! |
++-----------------------+--------------------------+
+| Hello! | WHAT ARE YOU LOOKING AT? |
++-----------------------+--------------------------+
-:class:`Rows` Objects
-=====================
-.. autoclass:: django_tables.rows.Rows
- :members: __init__, all, page, __iter__, __len__, count, __getitem__
+If you plan on returning HTML from a :meth:`~Column.render` method, you must
+remember to mark it as safe (otherwise it will be escaped when the table is
+rendered). This can be achieved by using the :func:`mark_safe` function.
+.. code-block:: python
-:class:`BoundRow` Objects
-=========================
+ >>> from django.utils.safestring import mark_safe
+ >>>
+ >>> class ImageColumn(tables.Column):
+ ... def render(self, **kwargs):
+ ... raw = super(AngryColumn, self).render(**kwargs)
+ ... return mark_safe('<img src="/media/img/%s.jpg" />' % raw)
+ ...
-.. autoclass:: django_tables.rows.BoundRow
- :members: __init__, values, __getitem__, __contains__, __iter__
.. _template_tags:
{% endfor %}
</tbody>
</table>
+
+
+API Reference
+=============
+
+:class:`Column` Objects:
+------------------------
+
+
+.. autoclass:: django_tables.columns.Column
+ :members: __init__, default, render
+
+
+:class:`Columns` Objects
+------------------------
+
+.. autoclass:: django_tables.columns.Columns
+ :members: __init__, all, items, names, sortable, visible, __iter__,
+ __contains__, __len__, __getitem__
+
+
+:class:`BoundColumn` Objects
+----------------------------
+
+.. autoclass:: django_tables.columns.BoundColumn
+ :members: __init__, table, column, name, accessor, default, formatter,
+ sortable, verbose_name, visible
+
+
+:class:`Rows` Objects
+---------------------
+
+.. autoclass:: django_tables.rows.Rows
+ :members: __init__, all, page, __iter__, __len__, count, __getitem__
+
+
+:class:`BoundRow` Objects
+-------------------------
+
+.. autoclass:: django_tables.rows.BoundRow
+ :members: __init__, values, __getitem__, __contains__, __iter__