/*.komodoproject
/MANIFEST
/dist/
+/build/
/docs/_build/
/django_tables.egg-info/
-# -*- coding: utf-8 -*-
-__version__ = (0, 2, 0, 'dev')
+# -*- coding: utf8 -*-
+# (major, minor, bugfix, "pre-alpha" | "alpha" | "beta" | "final", release | 0)
+VERSION = (0, 4, 0, 'alpha', 1)
def get_version():
- version = '%s.%s' % (__version__[0], __version__[1])
- if __version__[2]:
- version = '%s.%s' % (version, __version__[2])
- if __version__[3] != '':
- version = '%s %s' % (version, __version__[3])
+ version = '%s.%s' % (VERSION[0], VERSION[1])
+ if VERSION[2]:
+ version = '%s.%s' % (version, VERSION[2])
+ if VERSION[3:] == ('alpha', 0):
+ version = '%s pre-alpha' % version
+ else:
+ if VERSION[3] != 'final':
+ version = '%s %s %s' % (version, VERSION[3], VERSION[4])
return version
+
# We want to make get_version() available to setup.py even if Django is not
-# available or we are not inside a Django project (so we do distutils stuff).
+# available or we are not inside a Django project.
try:
- # this fails if project settings module isn't configured
- from django.contrib import admin
+ import django
except ImportError:
import warnings
- warnings.warn('django-tables requires Django to be configured (settings) '
- 'prior to use, however this has not been done. Version information '
- 'will still be available.')
+ warnings.warn('django-tables requires Django, however it is not installed.'
+ ' Version information will still be available.')
else:
+ try:
+ # http://docs.djangoproject.com/en/dev/topics/settings/ says::
+ #
+ # If you don't set DJANGO_SETTINGS_MODULE and don't call configure(),
+ # Django will raise an ImportError exception the first time a setting is
+ # accessed.
+ #
+ from django.conf import settings
+ settings.DEBUG # will raise ImportError if Django isn't configured
+ except ImportError:
+ # allow get_version() to remain available
+ import warnings
+ warnings.warn('django-tables requires Django to be configured... but '
+ "it isn't! A bunch of stuff won't work :(")
+
from tables import *
from columns import *
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 {}
class BoundColumn(StrAndUnicode):
- """'Runtime' version of ``Column`` that is bound to a table instance,
- and thus knows about the table's data. The difference between BoundColumn
- and Column, is a BoundColumn is aware of actual values (e.g. its name)
- where-as Column is not.
+ """A *runtime* version of :class:`Column`. The difference between
+ :class:`BoundColumn` and :class:`Column`, is that :class:`BoundColumn`
+ objects are of the relationship between a :class:`Column` and a
+ :class:`Table`. This means that it knows the *name* given to the
+ :class:`Column`.
+
+ For convenience, all :class:`Column` properties are available from this
+ class.
- For convenience, all Column properties are available from this class.
"""
def __init__(self, table, column, name):
- """*table* - the table in which this column exists
- *column* - the column class
- *name* – the variable name used when the column was defined in the
- table class
+ """Initialise a :class:`BoundColumn` object where:
+
+ * *table* - a :class:`Table` object in which this column exists
+ * *column* - a :class:`Column` object
+ * *name* – the variable name used when the column was added to the
+ :class:`Table` subclass
+
"""
- self.table = table
- self.column = column
- self.name = name
+ self._table = table
+ self._column = column
+ self._name = name
def __unicode__(self):
s = self.column.verbose_name or self.name.replace('_', ' ')
return capfirst(force_unicode(s))
+ @property
+ def table(self):
+ """Returns the :class:`Table` object that this column is part of."""
+ return self._table
+
+ @property
+ def column(self):
+ """Returns the :class:`Column` object for this column."""
+ return self._column
+
+ @property
+ def name(self):
+ """Returns the string used to identify this column."""
+ return self._name
+
@property
def accessor(self):
+ """Returns the string used to access data for this column out of the
+ data source.
+
+ """
return self.column.accessor or self.name
@property
def default(self):
+ """Returns the default value for this column."""
return self.column.default
@property
def formatter(self):
+ """Returns a function or ``None`` that represents the formatter for
+ this column.
+
+ """
return self.column.formatter
@property
def sortable(self):
+ """Returns a ``bool`` depending on whether this column is sortable."""
if self.column.sortable is not None:
return self.column.sortable
elif self.table._meta.sortable is not None:
@property
def verbose_name(self):
+ """Returns the verbose name for this column."""
return self.column.verbose_name
@property
def visible(self):
+ """Returns a ``bool`` depending on whether this column is visible."""
return self.column.visible
provides access to those columns in different ways (iterator,
item-based, filtered and unfiltered etc), stuff that would not be
possible with a simple iterator in the table class.
+
+ A :class:`Columns` object is a container for holding :class:`BoundColumn`
+ objects. It provides methods that make accessing columns easier than if
+ they were stored in a ``list`` or ``dict``. :class:`Columns` has a similar
+ API to a ``dict`` (it actually uses a :class:`SortedDict` interally).
+
+ At the moment you'll only come across this class when you access a
+ :attr:`Table.columns` property.
+
"""
def __init__(self, table):
+ """Initialise a :class:`Columns` object.
+
+ *table* must be a :class:`Table` object.
+
+ """
self.table = table
# ``self._columns`` attribute stores the bound columns (columns that
# have a real name, )
self._columns = new_columns
def all(self):
- """Iterate through all columns, regardless of visiblity (as
- opposed to ``__iter__``.
+ """Iterate through all :class:`BoundColumn` objects, regardless of
+ visiblity or sortability.
- This is used internally a lot.
"""
self._spawn_columns()
for column in self._columns.values():
yield column
def items(self):
+ """Return an iterator of ``(name, column)`` pairs (where *column* is a
+ :class:`BoundColumn` object).
+
+ """
self._spawn_columns()
for r in self._columns.items():
yield r
def names(self):
+ """Return an iterator of column names."""
self._spawn_columns()
for r in self._columns.keys():
yield r
- def index(self, name):
- self._spawn_columns()
- return self._columns.keyOrder.index(name)
-
def sortable(self):
- """Iterate through all sortable columns.
+ """Same as :meth:`all` but only returns sortable :class:`BoundColumn`
+ objects.
- This is primarily useful in templates, where iterating over the full
- set and checking {% if column.sortable %} can be problematic in
- conjunction with e.g. {{ forloop.last }} (the last column might not
+ This is useful in templates, where iterating over the full
+ set and checking ``{% if column.sortable %}`` can be problematic in
+ conjunction with e.g. ``{{ forloop.last }}`` (the last column might not
be the actual last that is rendered).
+
"""
for column in self.all():
if column.sortable:
yield column
- def __iter__(self):
- """Iterate through all *visible* bound columns.
+ def visible(self):
+ """Same as :meth:`sortable` but only returns visible
+ :class:`BoundColumn` objects.
+
+ This is geared towards table rendering.
- This is primarily geared towards table rendering.
"""
for column in self.all():
if column.visible:
yield column
+ def __iter__(self):
+ """Convenience API with identical functionality to :meth:`visible`."""
+ return self.visible()
+
def __contains__(self, item):
- """Check by both column object and column name."""
+ """Check if a column is contained within a :class:`Columns` object.
+
+ *item* can either be a :class:`BoundColumn` object, or the name of a
+ column.
+
+ """
self._spawn_columns()
if isinstance(item, basestring):
return item in self.names()
return item in self.all()
def __len__(self):
+ """Return how many :class:`BoundColumn` objects are contained."""
self._spawn_columns()
return len([1 for c in self._columns.values() if c.visible])
def __getitem__(self, index):
- """Return a column by name or index."""
+ """Retrieve a specific :class:`BoundColumn` object.
+
+ *index* can either be 0-indexed or the name of a column
+
+ .. code-block:: python
+
+ columns['speed'] # returns a bound column with name 'speed'
+ columns[0] # returns the first column
+
+ """
self._spawn_columns()
if isinstance(index, int):
return self._columns.value_for_index(index)
+# -*- coding: utf-8 -*-
+
class BoundRow(object):
- """Represents a single row of in a table.
+ """Represents a *specific* row in a table.
+
+ :class:`BoundRow` objects expose rendered versions of raw table data. This
+ means that formatting (via :attr:`Column.formatter` or an overridden
+ :meth:`Column.render` method) is applied to the values from the table's
+ data.
+
+ To access the rendered value of each cell in a row, just iterate over it:
+
+ .. code-block:: python
+
+ >>> import django_tables as tables
+ >>> class SimpleTable(tables.Table):
+ ... a = tables.Column()
+ ... b = tables.CheckBoxColumn(attrs={'name': 'my_chkbox'})
+ ...
+ >>> table = SimpleTable([{'a': 1, 'b': 2}])
+ >>> row = table.rows[0] # we only have one row, so let's use it
+ >>> for cell in row:
+ ... print cell
+ ...
+ 1
+ <input type="checkbox" name="my_chkbox" value="2" />
+
+ Alternatively you can treat it like a list and use indexing to retrieve a
+ specific cell. It should be noted that this will raise an IndexError on
+ failure.
+
+ .. code-block:: python
+
+ >>> row[0]
+ 1
+ >>> row[1]
+ u'<input type="checkbox" name="my_chkbox" value="2" />'
+ >>> row[2]
+ ...
+ IndexError: list index out of range
+
+ Finally you can also treat it like a dictionary and use column names as the
+ keys. This will raise KeyError on failure (unlike the above indexing using
+ integers).
+
+ .. code-block:: python
+
+ >>> row['a']
+ 1
+ >>> row['b']
+ u'<input type="checkbox" name="my_chkbox" value="2" />'
+ >>> row['c']
+ ...
+ KeyError: 'c'
- BoundRow provides a layer on top of the table data that exposes final
- rendered cell values for the table. This means that formatting (via
- Column.formatter or overridden Column.render in subclasses) applied to the
- values from the table's data.
"""
def __init__(self, table, data):
+ """Initialise a new :class:`BoundRow` object where:
+
+ * *table* is the :class:`Table` in which this row exists.
+ * *data* is a chunk of data that describes the information for this
+ row. A "chunk" of data might be a :class:`Model` object, a ``dict``,
+ or perhaps something else.
+
+ """
self.table = table
self.data = data
def __iter__(self):
+ """Iterate over the rendered values for cells in the row.
+
+ Under the hood this method just makes a call to :meth:`__getitem__` for
+ each cell.
+
+ """
for value in self.values:
yield value
custom = getattr(self.table, 'render_%s' % name, None)
if custom:
return custom(bound_column, self)
- return bound_column.column.render(self.table, bound_column, self)
+ return bound_column.column.render(table=self.table,
+ bound_column=bound_column,
+ bound_row=self)
def __contains__(self, item):
"""Check by both row object and column name."""
@property
def values(self):
for column in self.table.columns:
+ # this uses __getitem__, using the name (rather than the accessor)
+ # is correct – it's what __getitem__ expects.
yield self[column.name]
iterator in the table class.
"""
def __init__(self, table):
+ """Initialise a :class:`Rows` object. *table* is the :class:`Table`
+ object in which the rows exist.
+
+ """
self.table = table
def all(self):
- """Return all rows."""
+ """Return an iterable for all :class:`BoundRow` objects in the table.
+
+ """
for row in self.table.data:
yield BoundRow(self.table, row)
def page(self):
- """Return rows on current page (if paginated)."""
+ """If the table is paginated, return an iterable of :class:`BoundRow`
+ objects that appear on the current page, otherwise return None.
+
+ """
if not hasattr(self.table, 'page'):
return None
return iter(self.table.page.object_list)
def __iter__(self):
- return iter(self.all())
+ """Convience method for all()"""
+ return self.all()
def __len__(self):
+ """Returns the number of rows in the table."""
return len(self.table.data)
# for compatibility with QuerySetPaginator
count = __len__
def __getitem__(self, key):
+ """Allows normal list slicing syntax to be used."""
if isinstance(key, slice):
result = list()
for row in self.table.data[key]:
# -*- coding: utf8 -*-
import copy
-from django.db.models.query import QuerySet
from django.core.paginator import Paginator
from django.utils.datastructures import SortedDict
from django.http import Http404
from django.template.loader import get_template
from django.template import Context
+from django.utils.encoding import StrAndUnicode
from .utils import rmprefix, toggleprefix, OrderByTuple, Accessor
from .columns import Column
-from .memory import sort_table
from .rows import Rows, BoundRow
from .columns import Columns
set and a list of dicts.
"""
def __init__(self, data, table):
+ from django.db.models.query import QuerySet
self._data = data if not isinstance(data, QuerySet) else None
self._queryset = data if isinstance(data, QuerySet) else None
self._table = table
self.order_by = getattr(options, 'order_by', ())
-class Table(object):
+class Table(StrAndUnicode):
"""A collection of columns, plus their associated data rows."""
__metaclass__ = DeclarativeColumnsMetaclass
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+sys.path.insert(0, os.path.join(os.path.abspath('.'), os.pardir))
+import django_tables as tables
+sys.path.pop(0)
# -- General configuration -----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = []
+extensions = ['sphinx.ext.autodoc']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# built documents.
#
# The short X.Y version.
-version = '0.2'
+version = '.'.join(map(str, tables.VERSION[0:2]))
# The full version, including alpha/beta/rc tags.
-release = '0.2-dev'
+release = tables.get_version()
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+html_show_copyright = False
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
+.. default-domain:: py
+
=====================================================
django-tables - An app for creating HTML tables
=====================================================
... tz = tables.Column(verbose_name='Time Zone')
... visits = tables.Column()
-See :ref:`columns` for more information.
-
Providing data
--------------
{% load django_tables %}
{% render_table table %}
-See :ref:`template tags` for more information.
+See :ref:`template_tags` for more information.
Ordering
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}])
- >>> row = table.rows[0]
- >>> for cell in row:
+ >>> 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`
+---------------------------
-Columns
-=======
-
-The :class:`Columns` class provides an container for :class:`BoundColumn`
-instances. The simplest way to access the contained columns is to iterate over
-the instance:
-
-Each :class:`Table` instance has an instance as its :attr:`~Table.columns`
-property. Iterating over the instance yields only the visible columns. To
-access all columns (including those that are hidden), use the
-:func:`~Columns.all` method.
-
-Additionally, the :func:`~Columns.sortable` method provides access to all the
-sortable columns.
-
-
-Column options
---------------
-
-Each column takes a certain set of column-specific arguments (documented in the
-:ref:`column reference <columns.types>`).
-
-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
-
- This would not have worked:
-
- .. 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.
+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.
- 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.
-
- :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.
-
-
-Rows
-====
-
-Row objects
------------
-
-A row object represents a single row in a table.
-
-To access the rendered value of each cell in a row, you can iterate over the
-row:
+To change the way cells are rendered, simply override the
+:meth:`~Column.render` method.
.. code-block:: python
>>> import django_tables as tables
- >>> class SimpleTable(tables.Table):
- ... a = tables.Column()
- ... b = tables.CheckBoxColumn(attrs={'name': 'my_chkbox'})
+ >>>
+ >>> class AngryColumn(tables.Column):
+ ... def render(self, *args, **kwargs):
+ ... raw = super(AngryColumn, self).render(*args, **kwargs)
+ ... return raw.upper()
...
- >>> table = SimpleTable([{'a': 1, 'b': 2}])
- >>> row = table.rows[0] # we only have one row, so let's use it
- >>> for cell in row:
- ... print cell
+ >>> 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?',
+ ... }]
...
- 1
- <input type="checkbox" name="my_chkbox" value="2" />
+ >>> 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'
-Alternatively you can treat it like a list and use indexing to retrieve a
-specific cell. It should be noted that this will raise an IndexError on
-failure.
+Which, when displayed in a browser, would look something like this:
-.. code-block:: python
++-----------------------+--------------------------+
+| Normal | Angry |
++=======================+==========================+
+| May I have some food? | GIVE ME THE FOOD NOW! |
++-----------------------+--------------------------+
+| Hello! | WHAT ARE YOU LOOKING AT? |
++-----------------------+--------------------------+
- >>> row[0]
- 1
- >>> row[1]
- u'<input type="checkbox" name="my_chkbox" value="2" />'
- >>> row[2]
- ...
- IndexError: list index out of range
-Finally you can also treat it like a dictionary and use column names as the
-keys. This will raise KeyError on failure (unlike the above indexing using
-integers).
+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
- >>> row['a']
- 1
- >>> row['b']
- u'<input type="checkbox" name="my_chkbox" value="2" />'
- >>> row['c']
+ >>> 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)
...
- KeyError: 'c'
+.. _template_tags:
+
Template tags
=============
</table>
-Custom render methods
----------------------
+API Reference
+=============
-Often, displaying a raw value of a table cell is not good enough. For
-example, if your table has a ``rating`` column, you might want to show
-an image showing the given number of **stars**, rather than the plain
-numeric value.
+:class:`Column` Objects:
+------------------------
-While you can always write your templates so that the column in question
-is treated separately, either by conditionally checking for a column name,
-or by explicitely rendering each column manually (as opposed to simply
-looping over the ``rows`` and ``columns`` attributes), this is often
-tedious to do.
-Instead, you can opt to move certain formatting responsibilites into
-your Python code:
+.. autoclass:: django_tables.columns.Column
+ :members: __init__, default, render
-.. code-block:: python
- class BookTable(tables.ModelTable):
- name = tables.Column()
- rating = tables.Column(accessor='rating_int')
-
- def render_rating(self, bound_table):
- if bound_table.rating_count == 0:
- return '<img src="no-rating.png"/>'
- else:
- return '<img src="rating-%s.png"/>' % bound_table.rating_int
-
-When accessing ``table.rows[i].rating``, the ``render_rating`` method
-will be called. Note the following:
-
-- What is passed is underlying raw data object, in this case, the model
- instance. This gives you access to data values that may not have been defined
- as a column.
-- For the method name, the public name of the column must be used, not the
- internal field name. That is, it's ``render_rating``, not
- ``render_rating_int``.
-- The method is called whenever the cell value is retrieved by you, whether from
- Python code or within templates. However, operations by ``django-tables``,
- like sorting, always work with the raw data.
+: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__
# -*- coding: utf8 -*-
-from distutils.core import setup
+try:
+ from setuptools import setup
+except ImportError:
+ from distutils.core import setup
from distutils.command.install_data import install_data
from distutils.command.install import INSTALL_SCHEMES
import os
packages = packages,
data_files = data_files,
cmdclass = cmdclasses,
- requires = ['django(>=1.1)'],
- install_requires = ['django>=1.1']
+ requires = ['Django(>=1.1)'],
+ install_requires = ['Django>=1.1']
)