This commit is contained in:
Bradley Ayers 2012-09-30 12:57:36 +10:00
parent 0741abd31c
commit 512b91ead4
23 changed files with 774 additions and 662 deletions

8
.coveragerc Normal file
View File

@ -0,0 +1,8 @@
[run]
source =
django_tables2
tests
[html]
directory = reports/htmlcov

2
.gitignore vendored
View File

@ -14,4 +14,4 @@
/docs/_build/
/example/database.sqlite
/example/.env
/pylint_*
/report.pylint

View File

@ -2,18 +2,11 @@
from __future__ import absolute_import, unicode_literals
from django.db.models.fields import FieldDoesNotExist
from django.utils.datastructures import SortedDict
from django.utils.functional import curry
from django.utils.safestring import SafeData
from django_tables2.templatetags.django_tables2 import title
from django_tables2.utils import A, AttributeDict, OrderBy, OrderByTuple
from itertools import ifilter, islice
import warnings
import inspect
funcs = ifilter(curry(hasattr, inspect), ('getfullargspec', 'getargspec'))
getargspec = getattr(inspect, next(funcs))
del funcs
class Library(object):
@ -31,7 +24,7 @@ class Library(object):
"""
Return a column object suitable for model field.
:returns: column object of ``None``
:returns: column object of `None`
"""
# iterate in reverse order as columns are registered in order
# of least to most specialised (i.e. Column is registered
@ -57,50 +50,80 @@ class Column(object): # pylint: disable=R0902
"""
Represents a single column of a table.
:class:`Column` objects control the way a column (including the cells that
`.Column` objects control the way a column (including the cells that
fall within it) are rendered.
:param verbose_name: A human readable version of the column name. This
should not be title case. It is converted to title
case for use in column headers.
:type verbose_name: ``unicode``
:type accessor: :class:`basestring` or :class:`~.utils.Accessor`
:param accessor: An accessor that describes how to extract values for
this column from the :term:`table data`.
: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 is when a
:class:`QuerySet` is used as the table data (since
sorting is performed by the database).
.. attribute:: attrs
.. [1] The provided callable object must not expect to
receive any arguments.
:param order_by: Allows one or more accessors to be used for ordering
rather than ``accessor``.
:type order_by: :class:`unicode`, :class:`tuple`, :class:`~utils.Accessor`
:type visible: :class:`bool`
:param visible: If :const:`False`, this column will not be in HTML from
output generators (e.g. :meth:`as_html` or
``{% render_table %}``).
HTML attributes for elements that make up the column.
When a field is not visible, it is removed from the
table's :attr:`~Column.columns` iterable.
:type orderable: :class:`bool`
:param orderable: If :const:`False`, this column will not be allowed to
influence row ordering/sorting.
:type attrs: :class:`dict` object
:param attrs: HTML attributes to be added to components in the column
:type: `dict`
Supported ``attrs`` keys are:
This API is extended by subclasses to allow arbitrary HTML attributes
to be added to the output.
- *th* -- ``<th>`` element in header
- *td* -- ``<td>`` element in body
- *cell* -- fall back for ``<th>`` and ``<td>`` should they not be specified
By default `.Column` supports:
- *th* -- ``table/thead/th`` elements
- *td* -- ``table/tbody/tr/td`` elements
- *cell* -- fallback if *th* or *td* isn't defined
.. attribute:: accessor
An accessor that describes how to extract values for this column from
the :term:`table data`.
:type: `basestring` or `~.Accessor`
.. attribute:: default
The default value for the column. This can be a value or a callable
object [1]_. If an object in the data provides `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 is
when a `.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.
.. attribute:: order_by
Allows one or more accessors to be used for ordering rather than
*accessor*.
:type: `unicode`, `tuple`, `~.Accessor`
.. attribute:: orderable
If `False`, this column will not be allowed to influence row
ordering/sorting.
:type: `bool`
.. attribute:: verbose_name
A human readable version of the column name.
:type: `unicode`
This should not defined in title case, but rather natural case. It is
converted to title case for use in column headers.
.. attribute:: visible
If `True`, this column will be included in the HTML output.
:type: `bool`
"""
#: Tracks each time a Column instance is created. Used to retain order.
creation_counter = 0
@ -146,19 +169,20 @@ class Column(object): # pylint: disable=R0902
"""
The value used for the column heading (e.g. inside the ``<th>`` tag).
By default this titlises the column's :attr:`verbose_name`. If
``verbose_name`` is an instance of ``SafeData``, it's used unmodified.
By default this titlises the `~.Column.verbose_name`. If
`~.Column.verbose_name` is an instance of `~.safestring.SafeData`, it's
used unmodified.
:returns: ``unicode`` or ``None``
:returns: `unicode` or `None`
.. note::
This property typically isn't accessed directly when a table is
rendered. Instead, :attr:`.BoundColumn.header` is accessed which
in turn accesses this property. This allows the header to fallback
to the column name (it's only available on a :class:`.BoundColumn`
object hence accessing that first) when this property doesn't
return something useful.
rendered. Instead, `.BoundColumn.header` is accessed which in turn
accesses this property. This allows the header to fallback to the
column name (it's only available on a `.BoundColumn` object hence
accessing that first) when this property doesn't return something
useful.
"""
if self.verbose_name:
if isinstance(self.verbose_name, SafeData):
@ -171,22 +195,22 @@ class Column(object): # pylint: disable=R0902
"""
Returns the content for a specific cell.
This method can be overridden by :meth:`render_FOO` methods on the
table or by subclassing :class:`Column`.
This method can be overridden by :ref:`table.render_FOO` methods on the
table or by subclassing `.Column`.
:returns: `unicode`
If the value for this cell is in `self.empty_values`, this method is
If the value for this cell is in `.empty_values`, this method is
skipped and an appropriate default value is rendered instead.
Subclasses should set `empty_values` to `()` if they want to handle
all values in `render`.
Subclasses should set `.empty_values` to ``()`` if they want to handle
all values in `.render`.
"""
return value
@property
def sortable(self):
"""
*deprecated* -- use `orderable` instead.
*deprecated* -- use `.orderable` instead.
"""
warnings.warn('`sortable` is deprecated, use `orderable` instead.',
DeprecationWarning)
@ -195,14 +219,14 @@ class Column(object): # pylint: disable=R0902
@classmethod
def from_field(cls, field):
"""
Return a specialised column for the model field or ``None``.
Return a specialised column for the model field or `None`.
:param field: the field that needs a suitable column
:type field: model field instance
:returns: Column object or ``None``
:returns: `.Column` object or `None`
If the column isn't specialised for the given model field, it should
return ``None``. This gives other columns the opportunity to do better.
return `None`. This gives other columns the opportunity to do better.
If the column is specialised, it should return an instance of itself
that's configured appropriately for the field.
@ -215,22 +239,21 @@ class Column(object): # pylint: disable=R0902
class BoundColumn(object):
"""
A *run-time* version of :class:`.Column`. The difference between
``BoundColumn`` and ``Column``, is that ``BoundColumn`` objects include the
relationship between a ``Column`` and a :class:`.Table`. In practice, this
means that a ``BoundColumn`` knows the *"variable name"* given to the
``Column`` when it was declared on the ``Table``.
A *run-time* version of `.Column`. The difference between
`.BoundColumn` and `.Column`, is that `.BoundColumn` objects include the
relationship between a `.Column` and a `.Table`. In practice, this
means that a `.BoundColumn` knows the *"variable name"* given to the
`.Column` when it was declared on the `.Table`.
For convenience, all :class:`.Column` properties are available from this
class.
For convenience, all `.Column` properties are available from thisclass.
:type table: :class:`.Table` object
:type table: `.Table` object
:param table: the table in which this column exists
:type column: :class:`.Column` object
:type column: `.Column` object
:param column: the type of column
:type name: ``basestring`` object
:type name: `basestring` object
:param name: the variable name of the column used to when defining the
:class:`.Table`. In this example the name is ``age``:
`.Table`. In this example the name is ``age``:
.. code-block:: python
@ -257,7 +280,7 @@ class BoundColumn(object):
@property
def attrs(self):
"""
Proxy to ``Column.attrs`` but injects some values of our own.
Proxy to `.Column.attrs` but injects some values of our own.
A ``th`` and ``td`` are guaranteed to be defined (irrespective of
what's actually defined in the column attrs. This makes writing
@ -319,10 +342,10 @@ class BoundColumn(object):
@property
def order_by(self):
"""
Returns an :class:`OrderByTuple` of appropriately prefixed data source
Returns an `.OrderByTuple` of appropriately prefixed data source
keys used to sort this column.
See :meth:`.order_by_alias` for details.
See `.order_by_alias` for details.
"""
if self.column.order_by is not None:
order_by = self.column.order_by
@ -334,13 +357,13 @@ class BoundColumn(object):
@property
def order_by_alias(self):
"""
Returns an :class:`OrderBy` describing the current state of ordering
for this column.
Returns an `OrderBy` describing the current state of ordering for this
column.
The following attempts to explain the difference between ``order_by``
and ``order_by_alias``.
The following attempts to explain the difference between `order_by`
and `.order_by_alias`.
``order_by_alias`` returns and ``OrderBy`` instance that's based on
`.order_by_alias` returns and `.OrderBy` instance that's based on
the *name* of the column, rather than the keys used to order the table
data. Understanding the difference is essential.
@ -365,7 +388,7 @@ class BoundColumn(object):
>>> table.columns["name"].order_by
("-first_name", "-last_name")
The ``OrderBy`` returned has been patched to include an extra attribute
The `OrderBy` returned has been patched to include an extra attribute
``next``, which returns a version of the alias that would be
transitioned to if the user toggles sorting on this column, e.g.::
@ -402,7 +425,7 @@ class BoundColumn(object):
@property
def orderable(self):
"""
Return a ``bool`` depending on whether this column supports ordering.
Return a `bool` depending on whether this column supports ordering.
"""
if self.column.orderable is not None:
return self.column.orderable
@ -414,14 +437,14 @@ class BoundColumn(object):
Return the verbose name for this column, or fallback to prettified
column name.
If the table is using queryset data, then use the corresponding
model field's ``verbose_name``. If it's traversing a relationship,
If the table is using queryset data, then use the corresponding model
field's `~.db.Field.verbose_name`. If it's traversing a relationship,
then get the last field in the accessor (i.e. stop when the
relationship turns from ORM relationships to object attributes [e.g.
person.upper should stop at person]).
If the model field's ``verbose_name`` is a ``SafeData``, it's used
unmodified.
If the model field's `~.db.Field.verbose_name` is a
`~.safestring.SafeData`, it's used unmodified.
"""
# Favor an explicit defined verbose_name
if self.column.verbose_name:
@ -452,44 +475,37 @@ class BoundColumn(object):
@property
def visible(self):
"""
Returns a :class:`bool` depending on whether this column is visible.
Returns a `bool` depending on whether this column is visible.
"""
return self.column.visible
class BoundColumns(object):
"""
Container for spawning :class:`.BoundColumn` objects.
Container for spawning `.BoundColumn` objects.
This is bound to a table and provides its :attr:`.Table.columns` property.
This is bound to a table and provides its `.Table.columns` property.
It 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 ``BoundColumns`` object is a container for holding
``BoundColumn`` objects. It provides methods that make accessing
columns easier than if they were stored in a ``list`` or
``dict``. ``Columns`` has a similar API to a ``dict`` (it
actually uses a ``SortedDict`` interally).
A `BoundColumns` object is a container for holding `BoundColumn` objects.
It provides methods that make accessing columns easier than if they were
stored in a `list` or `dict`. `Columns` has a similar API to a `dict` (it
actually uses a `~django.utils.datastructures.SortedDict` interally).
At the moment you'll only come across this class when you access a
:attr:`.Table.columns` property.
`.Table.columns` property.
:type table: :class:`.Table` object
:type table: `.Table` object
:param table: the table containing the columns
"""
def __init__(self, table):
self.table = table
self.columns = SortedDict()
for name, column in self.table.base_columns.iteritems():
self.columns[name] = BoundColumn(self.table, column, name)
# Prepare each column's ``render`` function and its expected argument
# so they can be easily called when each row is iterated.
for name, bound_column in self.iteritems():
bound_column.render = getattr(self.table, 'render_' + bound_column.name,
bound_column.column.render)
bound_column._render_args = getargspec(bound_column.render).args[1:]
for name, column in table.base_columns.iteritems():
self.columns[name] = bc = BoundColumn(table, column, name)
bc.render = getattr(table, 'render_' + name, column.render)
def iternames(self):
return (name for name, column in self.iteritems())
@ -499,7 +515,7 @@ class BoundColumns(object):
def iterall(self):
"""
Return an iterator that exposes all :class:`.BoundColumn` objects,
Return an iterator that exposes all `.BoundColumn` objects,
regardless of visiblity or sortability.
"""
return (column for name, column in self.iteritems())
@ -510,11 +526,11 @@ class BoundColumns(object):
def iteritems(self):
"""
Return an iterator of ``(name, column)`` pairs (where ``column`` is a
:class:`.BoundColumn` object).
`BoundColumn`).
This method is the mechanism for retrieving columns that takes into
consideration all of the ordering and filtering modifiers that a table
supports (e.g. ``exclude`` and ``sequence``).
supports (e.g. `~Table.Meta.exclude` and `~Table.Meta.sequence`).
"""
for name in self.table.sequence:
if name not in self.table.exclude:
@ -525,7 +541,7 @@ class BoundColumns(object):
def iterorderable(self):
"""
Same as :meth:`.BoundColumns.all` but only returns orderable columns.
Same as `BoundColumns.all` but only returns orderable columns.
This is useful in templates, where iterating over the full
set and checking ``{% if column.sortable %}`` can be problematic in
@ -549,8 +565,8 @@ class BoundColumns(object):
def itervisible(self):
"""
Same as :meth:`.iterorderable` but only returns visible
:class:`.BoundColumn` objects.
Same as `.iterorderable` but only returns visible `.BoundColumn`
objects.
This is geared towards table rendering.
"""
@ -561,16 +577,15 @@ class BoundColumns(object):
def __iter__(self):
"""
Convenience API, alias of :meth:`.itervisible`.
Convenience API, alias of `.itervisible`.
"""
return self.itervisible()
def __contains__(self, item):
"""
Check if a column is contained within a :class:`.Columns` object.
Check if a column is contained within a `Columns` object.
*item* can either be a :class:`.BoundColumn` object, or the name of a
column.
*item* can either be a `BoundColumn` object, or the name of a column.
"""
if isinstance(item, basestring):
return item in self.iternames()
@ -587,7 +602,7 @@ class BoundColumns(object):
def __getitem__(self, index):
"""
Retrieve a specific :class:`BoundColumn` object.
Retrieve a specific `BoundColumn` object.
*index* can either be 0-indexed or the name of a column

View File

@ -12,15 +12,15 @@ class BooleanColumn(Column):
"""
A column suitable for rendering boolean data.
:param null: is ``None`` different from ``False``?
:type null: bool
:param null: is `None` different from `False`?
:type null: `bool`
:param yesno: text to display for True/False values, comma separated
:type yesno: iterable or string
Rendered values are wrapped in a ``<span>`` to allow customisation by
themes. By default the span is given the class ``true``, ``false``.
In addition to ``attrs`` keys supported by ``Column``, the following are
In addition to *attrs* keys supported by `.Column`, the following are
available:
- *span* -- adds attributes to the <span> tag

View File

@ -9,7 +9,7 @@ from .base import Column, library
@library.register
class CheckBoxColumn(Column):
"""
A subclass of :class:`.Column` that renders as a checkbox form input.
A subclass of `.Column` that renders as a checkbox form input.
This column allows a user to *select* a set of rows. The selection
information can then be used to apply some operation (e.g. "delete") onto
@ -22,8 +22,8 @@ class CheckBoxColumn(Column):
This class implements some sensible defaults:
- HTML input's ``name`` attribute is the :term:`column name` (can override
via ``attrs`` argument).
- ``orderable`` defaults to :const:`False`.
via *attrs* argument).
- *orderable* defaults to `False`.
.. note::
@ -32,7 +32,7 @@ class CheckBoxColumn(Column):
implemented. If you want something to actually happen, you'll need to
implement that yourself.
In addition to ``attrs`` keys supported by ``Column``, the following are
In addition to *attrs* keys supported by `.Column`, the following are
available:
- *input* -- ``<input>`` elements in both ``<td>`` and ``<th>``.

View File

@ -12,10 +12,10 @@ class DateColumn(TemplateColumn):
:param format: format string in same format as Django's ``date`` template
filter (optional)
:type format: ``unicode``
:param short: if ``format`` is not specified, use Django's
:type format: `unicode`
:param short: if *format* is not specified, use Django's
``SHORT_DATE_FORMAT`` setting, otherwise use ``DATE_FORMAT``
:type short: ``bool``
:type short: `bool`
"""
def __init__(self, format=None, short=True, *args, **kwargs): # pylint: disable=W0622
if format is None:

View File

@ -11,10 +11,10 @@ class DateTimeColumn(TemplateColumn):
A column that renders datetimes in the local timezone.
:param format: format string for datetime (optional)
:type format: ``unicode``
:param short: if ``format`` is not specifid, use Django's
:type format: `unicode`
:param short: if *format* is not specifid, use Django's
``SHORT_DATETIME_FORMAT``, else ``DATETIME_FORMAT``
:type short: ``bool``
:type short: `bool`
"""
def __init__(self, format=None, short=True, *args, **kwargs): # pylint: disable=W0622
if format is None:

View File

@ -8,11 +8,11 @@ from .linkcolumn import BaseLinkColumn
@library.register
class EmailColumn(BaseLinkColumn):
"""
A subclass of :class:`.BaseLinkColumn` that renders the cell value as a hyperlink.
A subclass of `.BaseLinkColumn` that renders the cell value as a hyperlink.
It's common to have a email value in a row hyperlinked to other page.
:param attrs: a :class:`dict` of HTML attributes that are added to
:param attrs: a `dict` of HTML attributes that are added to
the rendered ``<a href="...">...</a>`` tag
Example:

View File

@ -10,19 +10,25 @@ from .base import Column, library
@library.register
class FileColumn(Column):
"""
Renders a FieldFile (or other storage backend File) as a link.
Attempts to render `.FieldFile` (or other storage backend `.File`) as a
hyperlink.
In addition to ``attrs`` keys supported by ``Column``, the following are
available:
When the file is accessible via a URL, the file is rendered as a
hyperlink. The `.basename` is used as the text::
<a href="/media/path/to/receipt.pdf" title="path/to/receipt.pdf">receipt.pdf</a>
When unable to determine the URL, a ``span`` is used instead::
<span title="path/to/receipt.pdf">receipt.pdf</span>
`.Column.attrs` keys ``a`` and ``span`` can be used to add additional attributes.
:type verify_exists: bool
:param verify_exists: *try* to determine if the file actually exists.
:param verify_exists: attempt to determine if the file exists
- *a* -- ``<a>`` elements in ``<td>``
- *span* -- ``<span>`` elements in ``<td>`` (missing files)
if *verify_exists*, the HTML class ``exists`` or ``missing`` is added to
the element.
If *verify_exists*, the HTML class ``exists`` or ``missing`` is added to
the element to indicate the integrity of the storage.
"""
def __init__(self, verify_exists=True, **kwargs):
self.verify_exists = True

View File

@ -12,7 +12,7 @@ class BaseLinkColumn(Column):
"""
The base for other columns that render links.
Adds support for an ``a`` key in ``attrs`` which is added to the rendered
Adds support for an ``a`` key in *attrs** which is added to the rendered
``<a href="...">`` tag.
"""
def __init__(self, attrs=None, *args, **kwargs):
@ -53,22 +53,22 @@ class LinkColumn(BaseLinkColumn):
dedicated to that record.
The first arguments are identical to that of
:func:`django.core.urlresolvers.reverse` and allows an internal URL to be
described. The last argument ``attrs`` allows custom HTML attributes to
`~django.core.urlresolvers.reverse` and allows an internal URL to be
described. The last argument *attrs* allows custom HTML attributes to
be added to the rendered ``<a href="...">`` tag.
:param viewname: See :func:`django.core.urlresolvers.reverse`.
:param urlconf: See :func:`django.core.urlresolvers.reverse`.
:param args: See :func:`django.core.urlresolvers.reverse`. **
:param kwargs: See :func:`django.core.urlresolvers.reverse`. **
:param current_app: See :func:`django.core.urlresolvers.reverse`.
:param attrs: a :class:`dict` of HTML attributes that are added to
:param viewname: See `~django.core.urlresolvers.reverse`.
:param urlconf: See `~django.core.urlresolvers.reverse`.
:param args: See `~django.core.urlresolvers.reverse`. **
:param kwargs: See `~django.core.urlresolvers.reverse`. **
:param current_app: See `~django.core.urlresolvers.reverse`.
:param attrs: a `dict` of HTML attributes that are added to
the rendered ``<input type="checkbox" .../>`` tag
** In order to create a link to a URL that relies on information in the
current row, :class:`.Accessor` objects can be used in the ``args`` or
``kwargs`` arguments. The accessor will be resolved using the row's record
before ``reverse()`` is called.
current row, `.Accessor` objects can be used in the *args* or
*kwargs* arguments. The accessor will be resolved using the row's record
before `~django.core.urlresolvers.reverse` is called.
Example:
@ -89,7 +89,7 @@ class LinkColumn(BaseLinkColumn):
class PeopleTable(tables.Table):
name = tables.LinkColumn('people_detail', args=[A('pk')])
In addition to ``attrs`` keys supported by ``Column``, the following are
In addition to *attrs* keys supported by `.Column`, the following are
available:
- *a* -- ``<a>`` elements in ``<td>``.

View File

@ -8,7 +8,7 @@ from .base import Column, library
@library.register
class TemplateColumn(Column):
"""
A subclass of :class:`.Column` that renders some template code to use as
A subclass of `.Column` that renders some template code to use as
the cell value.
:type  template_code: `unicode`
@ -16,12 +16,12 @@ class TemplateColumn(Column):
:type  template_name: `unicode`
:param template_name: the name of the template to render
A :class:`django.templates.Template` object is created from the
A `~django.template.Template` object is created from the
*template_code* or *template_name* and rendered with a context containing:
- `record` -- data record for the current row
- `value` -- value from `record` that corresponds to the current column
- `default` -- appropriate default value to use as fallback
- *record* -- data record for the current row
- *value* -- value from `record` that corresponds to the current column
- *default* -- appropriate default value to use as fallback
Example:
@ -37,7 +37,7 @@ class TemplateColumn(Column):
.. important::
In order to use template tags or filters that require a
``RequestContext``, the table **must** be rendered via
`~django.template.RequestContext`, the table **must** be rendered via
:ref:`{% render_table %} <template-tags.render_table>`.
"""
empty_values = ()

View File

@ -8,26 +8,19 @@ from .linkcolumn import BaseLinkColumn
@library.register
class URLColumn(BaseLinkColumn):
"""
A subclass of :class:`.BaseLinkColumn` that renders the cell value as a hyperlink.
Renders URL values as hyperlinks.
It's common to have a URL value in a row hyperlinked to other page.
Example::
:param attrs: a :class:`dict` of HTML attributes that are added to
the rendered ``<a href="...">...</a>`` tag
>>> class CompaniesTable(tables.Table):
... www = tables.URLColumn()
...
>>> table = CompaniesTable([{"www": "http://google.com"}])
>>> table.rows[0]["www"]
u'<a href="http://google.com">http://google.com</a>'
Example:
.. code-block:: python
# models.py
class Person(models.Model):
name = models.CharField(max_length=200)
web = models.URLField()
# tables.py
class PeopleTable(tables.Table):
name = tables.Column()
web = tables.URLColumn()
Additional attributes for the ``<a>`` tag can be specified via
``attrs['a']``.
"""
def render(self, value):

View File

@ -6,20 +6,22 @@ class RequestConfig(object):
"""
A configurator that uses request data to setup a table.
:type paginate: ``dict`` or ``bool``
:type paginate: `dict` or `bool`
:param paginate: indicates whether to paginate, and if so, what default
values to use. If the value evaluates to ``False``,
pagination will be disabled. A ``dict`` can be used to
values to use. If the value evaluates to `False`,
pagination will be disabled. A `dict` can be used to
specify default values for the call to
:meth:`.tables.Table.paginate` (e.g. to define a default
``per_page`` value).
`~.tables.Table.paginate` (e.g. to define a default
*per_page* value).
A special ``silent`` item can be used to enable automatic
A special *silent* item can be used to enable automatic
handling of pagination exceptions using the following
algorithm:
- If ``PageNotAnInteger`` is raised, show the first page.
- If ``EmptyPage`` is raised, show the last page.
- If `~django.core.paginator.PageNotAnInteger`` is raised,
show the first page.
- If `~django.core.paginator.EmptyPage` is raised, show
the last page.
"""
def __init__(self, request, paginate=True):

View File

@ -1,17 +1,17 @@
# coding: utf-8
from django.db import models
from django.db.models.fields import FieldDoesNotExist
from .utils import A
from .utils import A, getargspec
class BoundRow(object):
"""
Represents a *specific* row in a table.
:class:`.BoundRow` objects are a container that make it easy to access the
`.BoundRow` objects are a container that make it easy to access the
final 'rendered' values for cells in a row. You can simply iterate over a
:class:`.BoundRow` object and it will take care to return values rendered
using the correct method (e.g. :meth:`.Column.render_FOO`)
`.BoundRow` object and it will take care to return values rendered
using the correct method (e.g. :ref:`table.render_FOO`)
To access the rendered value of each cell in a row, just iterate over it:
@ -58,10 +58,10 @@ class BoundRow(object):
...
KeyError: 'c'
:param table: is the :class:`Table` in which this row exists.
:param table: is the `.Table` in which this row exists.
:param record: a single record from the :term:`table data` that is used to
populate the row. A record could be a :class:`Model` object,
a :class:`dict`, or something else.
populate the row. A record could be a `~django.db.Model`
object, a `dict`, or something else.
"""
def __init__(self, record, table):
@ -70,7 +70,7 @@ class BoundRow(object):
@property
def table(self):
"""The associated :class:`.Table` object."""
"""The associated `.Table` object."""
return self._table
@property
@ -86,7 +86,7 @@ class BoundRow(object):
Iterate over the rendered values for cells in the row.
Under the hood this method just makes a call to
:meth:`.BoundRow.__getitem__` for each cell.
`.BoundRow.__getitem__` for each cell.
"""
for column, value in self.items():
# this uses __getitem__, using the name (rather than the accessor)
@ -124,19 +124,26 @@ class BoundRow(object):
if value in bound_column.column.empty_values:
return bound_column.default
kwargs = {
'value': lambda: value,
'record': lambda: self.record,
'column': lambda: bound_column.column,
'bound_column': lambda: bound_column,
'bound_row': lambda: self,
'table': lambda: self._table,
available = {
'value': value,
'record': self.record,
'column': bound_column.column,
'bound_column': bound_column,
'bound_row': self,
'table': self._table,
}
expected = {}
expected_kwargs = {}
for arg_name in bound_column._render_args:
expected_kwargs[arg_name] = kwargs[arg_name]()
return bound_column.render(**expected_kwargs)
# provide only the arguments expected by `render`
argspec = getargspec(bound_column.render)
if argspec.keywords:
expected = available
else:
for key, value in available.items():
if key in argspec.args[1:]:
expected[key] = value
return bound_column.render(**expected)
def __contains__(self, item):
"""Check by both row object and column name."""
@ -149,7 +156,7 @@ class BoundRow(object):
"""
Returns iterator yielding ``(bound_column, cell)`` pairs.
``cell`` is ``row[name]`` -- the rendered unicode value that should be
*cell* is ``row[name]`` -- the rendered unicode value that should be
``rendered within ``<td>``.
"""
for column in self.table.columns:
@ -158,12 +165,12 @@ class BoundRow(object):
class BoundRows(object):
"""
Container for spawning :class:`.BoundRow` objects.
Container for spawning `.BoundRow` objects.
:param data: iterable of records
:param table: the table in which the rows exist
This is used for :attr:`.Table.rows`.
This is used for `.Table.rows`.
"""
def __init__(self, data, table):
self.data = data
@ -178,8 +185,8 @@ class BoundRows(object):
def __getitem__(self, key):
"""
Slicing returns a new :class:`.BoundRows` instance, indexing returns
a single :class:`.BoundRow` instance.
Slicing returns a new `.BoundRows` instance, indexing returns a single
`.BoundRow` instance.
"""
container = BoundRows if isinstance(key, slice) else BoundRow
return container(self.data[key], table=self.table)

View File

@ -21,8 +21,8 @@ class TableData(object):
Exposes a consistent API for :term:`table data`.
:param data: iterable containing data for each row
:type data: :class:`QuerySet` or :class:`list` of :class:`dict`
:param table: :class:`.Table` object
:type data: `~django.db.query.QuerySet` or `list` of `dict`
:param table: `.Table` object
"""
def __init__(self, data, table):
self.table = table
@ -81,7 +81,7 @@ class TableData(object):
:param aliases: optionally prefixed names of columns ('-' indicates
descending order) in order of significance with
regard to data ordering.
:type aliases: :class:`~.utils.OrderByTuple`
:type aliases: `~.utils.OrderByTuple`
"""
accessors = []
for alias in aliases:
@ -109,7 +109,7 @@ class TableData(object):
def __getitem__(self, key):
"""
Slicing returns a new :class:`.TableData` instance, indexing returns a
Slicing returns a new `.TableData` instance, indexing returns a
single record.
"""
return self.data[key]
@ -119,9 +119,9 @@ class TableData(object):
"""
The full (singular) name for the data.
Queryset data has its model's ``Meta.verbose_name`` honored. List data
is checked for a ``verbose_name`` attribute, and falls back to using
``"item"``.
Queryset data has its model's `~django.db.Model.Meta.verbose_name`
honored. List data is checked for a ``verbose_name`` attribute, and
falls back to using ``"item"``.
"""
if hasattr(self, "queryset"):
return self.queryset.model._meta.verbose_name
@ -132,7 +132,7 @@ class TableData(object):
"""
The full (plural) name of the data.
This uses the same approach as ``verbose_name``.
This uses the same approach as `.verbose_name`.
"""
if hasattr(self, "queryset"):
return self.queryset.model._meta.verbose_name_plural
@ -141,9 +141,9 @@ class TableData(object):
class DeclarativeColumnsMetaclass(type):
"""
Metaclass that converts Column attributes on the class to a dictionary
called ``base_columns``, taking into account parent class ``base_columns``
as well.
Metaclass that converts `.Column` objects defined on a class to the
dictionary `.Table.base_columns`, taking into account parent class
``base_columns`` as well.
"""
def __new__(mcs, name, bases, attrs):
@ -198,12 +198,12 @@ class DeclarativeColumnsMetaclass(type):
class TableOptions(object):
"""
Extracts and exposes options for a :class:`.Table` from a ``class Meta``
when the table is defined. See ``Table`` for documentation on the impact of
Extracts and exposes options for a `.Table` from a `.Table.Meta`
when the table is defined. See `.Table` for documentation on the impact of
variables in this class.
:param options: options for a table
:type options: :class:`Meta` on a :class:`.Table`
:type options: `.Table.Meta` on a `.Table`
"""
# pylint: disable=R0902
def __init__(self, options=None):
@ -233,62 +233,125 @@ class TableOptions(object):
class Table(StrAndUnicode):
"""
A collection of columns, plus their associated data rows.
A representation of a table.
:type attrs: dict
:param attrs: A mapping of attributes to values that will be added to the
HTML ``<table>`` tag.
:type data: list or QuerySet-like
:param data: The :term:`table data`.
.. attribute:: attrs
:type exclude: iterable
:param exclude: A list of columns to be excluded from this table.
HTML attributes to add to the ``<table>`` tag.
:type order_by: None, tuple or string
:param order_by: sort the table based on these columns prior to display.
(default :attr:`.Table.Meta.order_by`)
:type: `dict`
:type order_by_field: string or None
:param order_by_field: The name of the querystring field used to control
the table ordering.
When accessing the attribute, the value is always returned as an
`.AttributeDict` to allow easily conversion to HTML.
:type page_field: string or None
:param page_field: The name of the querystring field used to control which
page of the table is displayed (used when a table is paginated).
:type per_page_field: string or None
:param per_page_field: The name of the querystring field used to control
how many records are displayed on each page of the table.
.. attribute:: columns
:type prefix: string
:param prefix: A prefix used on querystring arguments to allow multiple
tables to be used on a single page, without having conflicts
between querystring arguments. Depending on how the table is
rendered, will determine how the prefix is used. For example ``{%
render_table %}`` uses ``<prefix>-<argument>``.
The columns in the table.
:type sequence: iterable
:param sequence: The sequence/order of columns the columns (from left to
right). Items in the sequence must be column names, or the
*remaining items* symbol marker ``"..."`` (string containing three
periods). If this marker is used, not all columns need to be
defined.
:type: `.BoundColumns`
:type orderable: bool
:param orderable: Enable/disable column ordering on this table
:type template: string
:param template: the template to render when using {% render_table %}
(default ``django_tables2/table.html``)
.. attribute:: default
:type empty_text: string
:param empty_text: Empty text to render when the table has no data.
(default :attr:`.Table.Meta.empty_text`)
Text to render in empty cells (determined by `.Column.empty_values`,
default `.Table.Meta.default`)
:type: `unicode`
.. attribute:: empty_text
Empty text to render when the table has no data. (default
`.Table.Meta.empty_text`)
:type: `unicode`
.. attribute:: exclude
The names of columns that shouldn't be included in the table.
:type: iterable of `unicode`
.. attribute:: order_by_field
If not `None`, defines the name of the *order by* querystring field.
:type: `unicode`
.. attribute:: page
The current page in the context of pagination.
Added during the call to `.Table.paginate`.
.. attribute:: page_field
If not `None`, defines the name of the *current page* querystring
field.
:type: `unicode`
.. attribute:: paginator
The current paginator for the table.
Added during the call to `.Table.paginate`.
.. attribute:: per_page_field
If not `None`, defines the name of the *per page* querystring field.
:type: `unicode`
.. attribute:: prefix
A prefix for querystring fields to avoid name-clashes when using
multiple tables on a single page.
:type: `unicode`
.. attribute:: rows
The rows of the table (ignoring pagination).
:type: `.BoundRows`
.. attribute:: sequence
The sequence/order of columns the columns (from left to right).
:type: iterable
Items in the sequence must be :term:`column names <column name>`, or
``"..."`` (string containing three periods). ``...`` can be used as a
catch-all for columns that aren't specified.
.. attribute:: orderable
Enable/disable column ordering on this table
:type: `bool`
.. attribute:: template
The template to render when using ``{% render_table %}`` (default
``"django_tables2/table.html"``)
:type: `unicode`
:type default: unicode
:param default: Text to render in empty cells (determined by
:attr:`Column.empty_values`, default :attr:`.Table.Meta.default`)
"""
__metaclass__ = DeclarativeColumnsMetaclass
TableDataClass = TableData
@ -369,12 +432,6 @@ class Table(StrAndUnicode):
@property
def attrs(self):
"""
The attributes that should be applied to the ``<table>`` tag when
rendering HTML.
:rtype: :class:`~.utils.AttributeDict` object.
"""
return self._attrs if self._attrs is not None else self._meta.attrs
@attrs.setter
@ -437,17 +494,18 @@ class Table(StrAndUnicode):
Paginates the table using a paginator and creates a ``page`` property
containing information for the current page.
:type klass: Paginator ``class``
:type klass: Paginator class
:param klass: a paginator class to paginate the results
:type per_page: ``int``
:type per_page: `int`
:param per_page: how many records are displayed on each page
:type page: ``int``
:type page: `int`
:param page: which page should be displayed.
Extra arguments are passed to ``Paginator``.
Extra arguments are passed to the paginator.
Pagination exceptions (``EmptyPage`` and ``PageNotAnInteger``) may be
raised from this method and should be handled by the caller.
Pagination exceptions (`~django.core.paginator.EmptyPage` and
`~django.core.paginator.PageNotAnInteger`) may be raised from this
method and should be handled by the caller.
"""
per_page = per_page or self._meta.per_page
self.paginator = klass(self.rows, per_page, *args, **kwargs)

View File

@ -21,7 +21,7 @@ kwarg_re = re.compile(r"(?:(.+)=)?(.+)")
def token_kwargs(bits, parser):
"""
Based on Django's ``django.template.defaulttags.token_kwargs``, but with a
Based on Django's `~django.template.defaulttags.token_kwargs`, but with a
few changes:
- No legacy mode.
@ -70,7 +70,7 @@ def set_url_param(parser, token):
{% set_url_param name="help" age=20 %}
?name=help&age=20
**Deprecated** as of 0.7.0, use ``querystring``.
**Deprecated** as of 0.7.0, use `querystring`.
"""
bits = token.contents.split()
qschanges = {}
@ -207,7 +207,7 @@ def render_table(parser, token):
"""
Render a HTML table.
The tag can be given either a ``Table`` object, or a queryset. An optional
The tag can be given either a `.Table` object, or a queryset. An optional
second argument can specify the template to use.
Example::
@ -216,7 +216,7 @@ def render_table(parser, token):
{% render_table table "custom.html" %}
{% render_table user_queryset %}
When given a queryset, a ``Table`` class is generated dynamically as
When given a queryset, a `.Table` class is generated dynamically as
follows::
class OnTheFlyTable(tables.Table):
@ -224,10 +224,10 @@ def render_table(parser, token):
model = queryset.model
attrs = {"class": "paleblue"}
For configuration beyond this, a ``Table`` class must be manually defined,
For configuration beyond this, a `.Table` class must be manually defined,
instantiated, and passed to this tag.
The context should include a ``request`` variable containing the current
The context should include a *request* variable containing the current
request. This allows pagination URLs to be created without clobbering the
existing querystring.
"""
@ -266,8 +266,9 @@ def title(value):
"""
A slightly better title template filter.
Same as Django's builtin ``title`` filter, but operates on individual words
and leaves words unchanged if they already have a capital letter.
Same as Django's builtin `~django.template.defaultfilters.title` filter,
but operates on individual words and leaves words unchanged if they already
have a capital letter.
"""
title_word = lambda w: w if RE_UPPERCASE.search(w) else old_title(w)
return re.sub('(\S+)', lambda m: title_word(m.group(0)), value)

View File

@ -1,20 +1,22 @@
# coding: utf-8
from __future__ import absolute_import, unicode_literals
from django.core.handlers.wsgi import WSGIRequest
from django.utils.functional import curry
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.test.client import FakePayload
from itertools import chain
from itertools import chain, ifilter
import inspect
from StringIO import StringIO
import warnings
class Sequence(list):
"""
Represents a column sequence, e.g. ("first_name", "...", "last_name")
Represents a column sequence, e.g. ``("first_name", "...", "last_name")``
This is used to represent ``Table.Meta.sequence`` or the Table
constructors's ``sequence`` keyword argument.
This is used to represent `.Table.Meta.sequence` or the `.Table`
constructors's *sequence* keyword argument.
The sequence must be a list of column names and is used to specify the
order of the columns on a table. Optionally a "..." item can be inserted,
@ -23,10 +25,10 @@ class Sequence(list):
"""
def expand(self, columns):
"""
Expands the "..." item in the sequence into the appropriate column
Expands the ``"..."`` item in the sequence into the appropriate column
names that should be placed there.
:raises: ``ValueError`` if the sequence is invalid for the columns.
:raises: `ValueError` if the sequence is invalid for the columns.
"""
ellipses = self.count("...")
if ellipses > 1:
@ -52,8 +54,8 @@ class Sequence(list):
class OrderBy(str):
"""
A single item in an :class:`.OrderByTuple` object. This class is
essentially just a :class:`str` with some extra properties.
A single item in an `.OrderByTuple` object. This class is
essentially just a `str` with some extra properties.
"""
@property
def bare(self):
@ -65,14 +67,14 @@ class OrderBy(str):
Example: ``age`` is the bare form of ``-age``
:rtype: :class:`.OrderBy` object
:rtype: `.OrderBy` object
"""
return OrderBy(self[1:]) if self[:1] == '-' else self
@property
def opposite(self):
"""
Return an :class:`.OrderBy` object with an opposite sort influence.
Return an `.OrderBy` object with an opposite sort influence.
Example:
@ -82,35 +84,35 @@ class OrderBy(str):
>>> order_by.opposite
'-name'
:rtype: :class:`.OrderBy` object
:rtype: `.OrderBy` object
"""
return OrderBy(self[1:]) if self.is_descending else OrderBy('-' + self)
@property
def is_descending(self):
"""
Return :const:`True` if this object induces *descending* ordering
Return `True` if this object induces *descending* ordering
:rtype: :class:`bool`
:rtype: `bool`
"""
return self.startswith('-')
@property
def is_ascending(self):
"""
Return :const:`True` if this object induces *ascending* ordering.
Return `True` if this object induces *ascending* ordering.
:returns: :class:`bool`
:returns: `bool`
"""
return not self.is_descending
class OrderByTuple(tuple):
"""Stores ordering as (as :class:`.OrderBy` objects). The
:attr:`django_tables2.tables.Table.order_by` property is always converted
to an :class:`.OrderByTuple` object.
"""Stores ordering as (as `.OrderBy` objects). The
`~django_tables2.tables.Table.order_by` property is always converted
to an `.OrderByTuple` object.
This class is essentially just a :class:`tuple` with some useful extras.
This class is essentially just a `tuple` with some useful extras.
Example:
@ -156,7 +158,7 @@ class OrderByTuple(tuple):
True
:param name: The name of a column. (optionally prefixed)
:returns: :class:`bool`
:returns: `bool`
"""
name = OrderBy(name).bare
for order_by in self:
@ -166,7 +168,7 @@ class OrderByTuple(tuple):
def __getitem__(self, index):
"""
Allows an :class:`.OrderBy` object to be extracted via named or integer
Allows an `.OrderBy` object to be extracted via named or integer
based indexing.
When using named based indexing, it's fine to used a prefixed named.
@ -181,7 +183,7 @@ class OrderByTuple(tuple):
>>> x['-age']
'-age'
:rtype: :class:`.OrderBy` object
:rtype: `.OrderBy` object
"""
if isinstance(index, basestring):
for order_by in self:
@ -193,8 +195,8 @@ class OrderByTuple(tuple):
@property
def cmp(self):
"""
Return a function for use with :meth:`list.sort()` that implements this
object's ordering. This is used to sort non-:class:`QuerySet` based
Return a function for use with `list.sort` that implements this
object's ordering. This is used to sort non-`.QuerySet` based
:term:`table data`.
:rtype: function
@ -232,7 +234,7 @@ class OrderByTuple(tuple):
@property
def opposite(self):
"""
Return version with each :class:`OrderBy` prefix toggled.
Return version with each `.OrderBy` prefix toggled.
Example:
@ -248,7 +250,7 @@ class OrderByTuple(tuple):
class Accessor(str):
"""
A string describing a path from one object to another via attribute/index
accesses. For convenience, the class has an alias ``A`` to allow for more concise code.
accesses. For convenience, the class has an alias `.A` to allow for more concise code.
Relations are separated by a ``.`` character.
"""
@ -263,24 +265,25 @@ class Accessor(str):
.. code-block:: python
>>> x = Accessor('__len__`')
>>> x = Accessor('__len__')
>>> x.resolve('brad')
4
>>> x = Accessor('0.upper')
>>> x.resolve('brad')
'B'
:type context: :class:`object`
:type context: `object`
:param context: The root/first object to traverse.
:type safe: `bool`
:param safe: Don't call anything with `alters_data = True`
:param safe: Don't call anything with ``alters_data = True``
:type quiet: bool
:param quiet: Smother all exceptions and instead return `None`
:returns: target object
:raises: anything `getattr(a, "b")` raises, e.g. `TypeError`,
`AttributeError`, `KeyError`, `ValueError`
:raises: anything ``getattr(a, "b")`` raises, e.g. `TypeError`,
`AttributeError`, `KeyError`, `ValueError` (unless *quiet* ==
`True`)
:meth:`~.Accessor.resolve` attempts lookups in the following order:
`~.Accessor.resolve` attempts lookups in the following order:
- dictionary (e.g. ``obj[related]``)
- attribute (e.g. ``obj.related``)
@ -334,11 +337,11 @@ A = Accessor # alias
class AttributeDict(dict):
"""
A wrapper around :class:`dict` that knows how to render itself as HTML
A wrapper around `dict` that knows how to render itself as HTML
style tag attributes.
The returned string is marked safe, so it can be used safely in a template.
See :meth:`.as_html` for a usage example.
See `.as_html` for a usage example.
"""
def as_html(self):
"""
@ -353,7 +356,7 @@ class AttributeDict(dict):
>>> attrs.as_html()
'class="mytable" id="someid"'
:rtype: :class:`~django.utils.safestring.SafeUnicode` object
:rtype: `~django.utils.safestring.SafeUnicode` object
"""
return mark_safe(' '.join(['%s="%s"' % (k, escape(v))
@ -374,7 +377,7 @@ def segment(sequence, aliases):
"""
Translates a flat sequence of items into a set of prefixed aliases.
This allows the value set by `QuerySet.order_by()` to be translated into
This allows the value set by `.QuerySet.order_by` to be translated into
a list of columns that would have the same result. These are called
"order by aliases" which are optionally prefixed column names.
@ -417,6 +420,8 @@ class cached_property(object): # pylint: disable=C0103
Taken directly from Django 1.4.
"""
def __init__(self, func):
from functools import wraps
wraps(func)(self)
self.func = func
def __get__(self, instance, cls):
@ -424,12 +429,17 @@ class cached_property(object): # pylint: disable=C0103
return res
funcs = ifilter(curry(hasattr, inspect), ('getfullargspec', 'getargspec'))
getargspec = getattr(inspect, next(funcs))
del funcs
def build_request(uri='/'):
"""
Return a fresh HTTP GET / request.
This is essentially a heavily cutdown version of Django 1.3's
``django.test.client.RequestFactory``.
`~django.test.client.RequestFactory`.
"""
path, _, querystring = uri.partition('?')
return WSGIRequest({

View File

@ -7,22 +7,23 @@ from .config import RequestConfig
class SingleTableMixin(object):
"""
Adds a Table object to the context. Typically used with
``TemplateResponseMixin``.
`.TemplateResponseMixin`.
:param table_class: table class
:type table_class: subclass of ``django_tables2.Table``
:type table_class: subclass of `.Table`
:param table_data: data used to populate the table
:type table_data: any compatible data source
:param context_table_name: name of the table's template variable (default:
"table")
:type context_table_name: ``string``
:param table_pagination: controls table pagination. If a dict, passed as
the ``paginate`` keyword argument to
``RequestConfig()``. As such, any *non-False*
:type context_table_name: `unicode`
:param table_pagination: controls table pagination. If a `dict`, passed as
the *paginate* keyword argument to
`.RequestConfig`. As such, any non-`False`
value enables pagination.
This mixin plays nice with the Django's ``MultipleObjectMixin`` by using
``get_queryset()`` as a fallback for the table data source.
This mixin plays nice with the Django's`.MultipleObjectMixin` by using
`.get_queryset`` as a fallback for the table data source.
"""
table_class = None
table_data = None
@ -80,7 +81,7 @@ class SingleTableMixin(object):
def get_context_data(self, **kwargs):
"""
Overriden version of ``TemplateResponseMixin`` to inject the table into
Overriden version of `.TemplateResponseMixin` to inject the table into
the template's context.
"""
context = super(SingleTableMixin, self).get_context_data(**kwargs)
@ -91,5 +92,5 @@ class SingleTableMixin(object):
class SingleTableView(SingleTableMixin, ListView):
"""
Generic view that renders a template and passes in a ``Table`` object.
Generic view that renders a template and passes in a `.Table` object.
"""

View File

@ -19,6 +19,9 @@ with open('../django_tables2/__init__.py', 'rb') as f:
version = release.rpartition('.')[0]
default_role = "py:obj"
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',

View File

@ -16,6 +16,12 @@ django-tables2 turns data into HTML tables. Features:
Report bugs at http://github.com/bradleyayers/django-tables2/issues
.. toctree::
:hidden:
internal
Tutorial
========
@ -59,8 +65,8 @@ Hook the view up in your URLs, and load the page, you should see:
:align: center
:alt: An example table rendered using django-tables2
While simple, passing a queryset directly to ``render_table`` doesn't allow for
any customisation. For that, you must define a ``Table`` class.
While simple, passing a queryset directly to ``{% render_table %}`` doesn't
allow for any customisation. For that, you must define a `.Table` class.
::
@ -91,10 +97,11 @@ adding it to the context.
RequestConfig(request).configure(table)
return render(request, 'people.html', {'table': table})
Using ``RequestConfig`` automatically pulls values from ``request.GET`` and
Using `.RequestConfig` automatically pulls values from ``request.GET`` and
updates the table accordingly. This enables data ordering and pagination.
Rather than passing a queryset to ``render_table``, instead pass the table.
Rather than passing a queryset to ``{% render_table %}``, instead pass the
table.
.. sourcecode:: django
@ -253,7 +260,7 @@ however when mixing auto-generated columns (via `Table.Meta.model`) with
manually declared columns, the column sequence becomes ambiguous.
To resolve the ambiguity, columns sequence can be declard via the
``Table.Meta.sequence`` option::
`.Table.Meta.sequence` option::
class PersonTable(tables.Table):
selection = tables.CheckBoxColumn(accessor="pk", orderable=False)
@ -271,10 +278,8 @@ should inserted at that location. As such it can be used at most once.
Customising column headings
===========================
The header cell for each column comes from the column's
:meth:`~django_tables2.columns.BoundColumn.header` method. By default this
method returns a titlised version of the column's ``verbose_name``.
The header cell for each column comes from `~.Column.header`. By default this
method returns a titlised version of the `~.Column.verbose_name`.
When using queryset data and a verbose name hasn't been explicitly
defined for a column, the corresponding model field's verbose name will be
@ -305,7 +310,7 @@ Consider the following:
As you can see in the last example (region name), the results are not always
desirable when an accessor is used to cross relationships. To get around this
be careful to define a ``verbose_name`` on such columns.
be careful to define `.Column.verbose_name`.
.. _pagination:
@ -323,7 +328,7 @@ pass in the current page number, e.g.
table.paginate(page=request.GET.get('page', 1), per_page=25)
return render(request, 'people_listing.html', {'table': table})
If you're using ``RequestConfig``, pass pagination options to the constructor,
If you're using `.RequestConfig`, pass pagination options to the constructor,
e.g.:
.. sourcecode:: python
@ -343,11 +348,113 @@ Various options are available for changing the way the table is :term:`rendered
<render>`. Each approach has a different balance of ease-of-use and
flexibility.
.. _table.render_foo:
:meth:`Table.render_FOO` methods
--------------------------------
To change how a column is rendered, implement a ``render_FOO`` method on the
table (where ``FOO`` is the :term:`column name`). This approach is suitable if
you have a one-off change that you don't want to use in multiple tables.
Supported keyword arguments include:
- ``record`` -- the entire record for the row from the :term:`table data`
- ``value`` -- the value for the cell retrieved from the :term:`table data`
- ``column`` -- the `.Column` object
- ``bound_column`` -- the `.BoundColumn` object
- ``bound_row`` -- the `.BoundRow` object
- ``table`` -- alias for ``self``
Here's an example where the first column displays the current row number::
>>> 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(SimpleTable, self).__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}])
>>> for cell in table.rows[0]:
... print cell
...
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.
.. important::
`render` methods are *only* called if the value for a cell is determined to
be not an :term:`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).
.. _subclassing-column:
Subclassing `.Column`
---------------------
Defining a column subclass allows functionality to be reused across tables.
Columns have a `render` method that behaves the same as :ref:`table.render_foo`
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)
>>> table.as_html()
u'<table><thead><tr><th>Normal</th><th>Upper</th></tr></thead><tbody><tr><td>Hi there!</td><td>HI THERE!</td></tr></tbody></table>\n'
See :ref:`table.render_foo` for a list of arguments that can be accepted.
For complicated columns, you may want to return HTML from the
:meth:`~Column.render` method. This is fine, but be sure to mark the string as
safe to avoid it being escaped::
>>> from django.utils.safestring import mark_safe
>>> from django.utils.html import escape
>>>
>>> class ImageColumn(tables.Column):
... def render(self, value):
... return mark_safe('<img src="/media/img/%s.jpg" />'
... % escape(value))
...
.. _css:
CSS
---
In order to use CSS to style a table, you'll probably want to add a
``class`` or ``id`` attribute to the ``<table>`` element. ``django-tables2`` has
``class`` or ``id`` attribute to the ``<table>`` element. django-tables2 has
a hook that allows abitrary attributes to be added to the ``<table>`` tag.
.. sourcecode:: python
@ -364,62 +471,16 @@ a hook that allows abitrary attributes to be added to the ``<table>`` tag.
>>> table.as_html()
'<table class="mytable">...'
.. _custom-template:
.. _table.render_foo:
Custom Template
---------------
:meth:`Table.render_FOO` Methods
--------------------------------
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
`.Table` subclass into your own template, and render it yourself.
If you want to adjust the way table cells in a particular column are rendered,
you can implement a ``render_FOO`` method. ``FOO`` should be the
:term:`name <column name>` of the column.
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).
Supported keyword arguments include: (only the arguments declared will be passed)
- ``record`` -- the entire record for the row from the :term:`table data`
- ``value`` -- the value for the cell retrieved from the :term:`table data`
- ``column`` -- the :class:`.Column` object
- ``bound_column`` -- the :class:`.BoundColumn` object
- ``bound_row`` -- the :class:`.BoundRow` object
- ``table`` -- alias for ``self``
Example::
>>> import django_tables2 as tables
>>> class SimpleTable(tables.Table):
... row_number = tables.Column()
... id = tables.Column()
... age = tables.Column()
...
... def render_row_number(self):
... value = getattr(self, '_counter', 0)
... self._counter = value + 1
... return 'Row %d' % value
...
... def render_id(self, value):
... return '<%s>' % value
...
>>> table = SimpleTable([{'age': 31, 'id': 10}, {'age': 34, 'id': 11}])
>>> for cell in table.rows[0]:
... print cell
...
Row 0
<10>
31
.. note::
Due to the implementation of dynamic argument passing, any "catch-extra"
arguments (e.g. ``*args`` or ``**kwargs``) will not recieve any arguments.
This is because `inspect.getargsspec()`__ is used to check what arguments a
``render_FOO`` method expect, and only to supply those.
.. __: http://docs.python.org/library/inspect.html#inspect.getargspec
Have a look at the ``django_tables2/table.html`` template for an example.
.. _query-string-fields:
@ -432,9 +493,9 @@ preferences.
The names of the querystring variables are configurable via the options:
- ``order_by_field`` -- default: ``sort``
- ``page_field`` -- default: ``page``
- ``per_page_field`` -- default: ``per_page``, **note:** this field currently
- ``order_by_field`` -- default: ``"sort"``
- ``page_field`` -- default: ``"page"``
- ``per_page_field`` -- default: ``"per_page"``, **note:** this field currently
isn't used by ``{% render_table %}``
Each of these can be specified in three places:
@ -462,7 +523,7 @@ fields with a table-specific name. e.g.
Column attributes
=================
Column attributes can be specified using the :class:`dict` with specific keys.
Column attributes can be specified using the `dict` with specific keys.
The dict defines HTML attributes for one of more elements within the column.
Depending on the column, different elements are supported, however ``th``,
``td``, and ``cell`` are supported universally.
@ -493,93 +554,18 @@ Built-in columns
For common use-cases the following columns are included:
- :class:`.BooleanColumn` -- renders boolean values
- :class:`.Column` -- generic column
- :class:`.CheckBoxColumn` -- renders checkbox form inputs
- :class:`.DateColumn` -- date formatting
- :class:`.DateTimeColumn` -- datetime formatting in the local timezone
- :class:`.FileColumn` -- renders files as links
- :class:`.EmailColumn` -- renders ``<a href="mailto:...">`` tags
- :class:`.LinkColumn` -- renders ``<a href="...">`` tags (absolute url)
- :class:`.TemplateColumn` -- renders template code
- :class:`.URLColumn` -- renders ``<a href="...">`` tags (compose a django url)
- `.BooleanColumn` -- renders boolean values
- `.Column` -- generic column
- `.CheckBoxColumn` -- renders checkbox form inputs
- `.DateColumn` -- date formatting
- `.DateTimeColumn` -- datetime formatting in the local timezone
- `.FileColumn` -- renders files as links
- `.EmailColumn` -- renders ``<a href="mailto:...">`` tags
- `.LinkColumn` -- renders ``<a href="...">`` tags (absolute url)
- `.TemplateColumn` -- renders template code
- `.URLColumn` -- renders ``<a href="...">`` tags (compose a django url)
.. _subclassing-column:
Subclassing :class:`Column`
---------------------------
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.
To change the way cells are rendered, simply override the
:meth:`~Column.render` method.
.. sourcecode:: python
>>> import django_tables2 as tables
>>>
>>> class AngryColumn(tables.Column):
... def render(self, value):
... return value.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'
See :ref:`table.render_foo` for a list of arguments that can be accepted.
Which, when displayed in a browser, would look something like this:
+-----------------------+--------------------------+
| Normal | Angry |
+=======================+==========================+
| May I have some food? | GIVE ME THE FOOD NOW! |
+-----------------------+--------------------------+
| Hello! | WHAT ARE YOU LOOKING AT? |
+-----------------------+--------------------------+
For complicated columns, it's sometimes necessary to return HTML from a
:meth:`~Column.render` method, but the string must be marked as safe (otherwise
it will be escaped when the table is rendered). This can be achieved by using
the :func:`mark_safe` function.
.. sourcecode:: python
>>> from django.utils.safestring import mark_safe
>>>
>>> class ImageColumn(tables.Column):
... def render(self, value):
... return mark_safe('<img src="/media/img/%s.jpg" />' % value)
...
.. _custom-template:
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.
Have a look at ``django_tables2/templates/django_tables2/table.html`` for an
example.
.. _template_tags:
Template tags
@ -590,7 +576,7 @@ Template tags
render_table
------------
Renders a :class:`~django_tables2.tables.Table` object to HTML and enables as
Renders a `~django_tables2.tables.Table` object to HTML and enables as
many features in the output as possible.
.. sourcecode:: django
@ -602,17 +588,17 @@ many features in the output as possible.
{% render_table table "path/to/custom_table_template.html" %}
If the second argument (template path) is given, the template will be rendered
with a ``RequestContext`` and the table will be in the variable ``table``.
with a `.RequestContext` and the table will be in the variable ``table``.
.. note::
This tag temporarily modifies the ``Table`` object while it is being
This tag temporarily modifies the `.Table` object while it is being
rendered. It adds a ``request`` attribute to the table, which allows
:class:`Column` objects to have access to a ``RequestContext``. See
:class:`.TemplateColumn` for an example.
`.Column` objects to have access to a `.RequestContext`. See
`.TemplateColumn` for an example.
This tag requires that the template in which it's rendered contains the
``HttpRequest`` inside a ``request`` variable. This can be achieved by ensuring
`~.http.HttpRequest` inside a ``request`` variable. This can be achieved by ensuring
the ``TEMPLATE_CONTEXT_PROCESSORS`` setting contains
``"django.core.context_processors.request"``. By default it is not included,
and the setting itself is not even defined within your project's
@ -680,7 +666,7 @@ Class Based Generic Mixins
Django 1.3 introduced `class based views`__ as a mechanism to reduce the
repetition in view code. django-tables2 comes with a single class based view
mixin: ``SingleTableMixin``. It makes it trivial to incorporate a table into a
mixin: `.SingleTableMixin`. It makes it trivial to incorporate a table into a
view/template.
The following view parameters are supported:
@ -688,7 +674,7 @@ The following view parameters are supported:
- ``table_class`` - the table class to use, e.g. ``SimpleTable``
- ``table_data`` (or ``get_table_data()``) -- the data used to populate the table
- ``context_table_name`` -- the name of template variable containing the table object
- ``table_pagination`` -- pagination options to pass to :class:`RequestConfig`
- ``table_pagination`` -- pagination options to pass to `.RequestConfig`
.. __: https://docs.djangoproject.com/en/1.3/topics/class-based-views/
@ -722,27 +708,27 @@ The template could then be as simple as:
{% render_table table %}
Such little code is possible due to the example above taking advantage of
default values and ``SimpleTableMixin``'s eagarness at finding data sources
default values and `.SimpleTableMixin`'s eagarness at finding data sources
when one isn't explicitly defined.
.. note::
If you need more than one table on a page, use ``SingleTableView`` and use
``get_context_data()`` to initialise the other tables and add them to the
If you need more than one table on a page, use `.SingleTableView` and use
`.get_context_data` to initialise the other tables and add them to the
context.
.. note::
You don't have to base your view on ``ListView``, you're able to mix
``SingleTableMixin`` directly.
You don't have to base your view on `ListView`, you're able to mix
`SingleTableMixin` directly.
Table Mixins
============
It's possible to create a mixin for a table that overrides something, however
unless it itself is a subclass of :class:`.Table` class
variable instances of :class:`.Column` will **not** be added to the class which is using the mixin.
unless it itself is a subclass of `.Table` class variable instances of
`.Column` will **not** be added to the class which is using the mixin.
Example::
@ -756,7 +742,7 @@ Example::
['name']
To have a mixin contribute a column, it needs to be a subclass of
:class:`~django_tables2.tables.Table`. With this in mind the previous example
`~django_tables2.tables.Table`. With this in mind the previous example
*should* have been written as follows::
>>> class UsefulMixin(tables.Table):
@ -774,103 +760,60 @@ To have a mixin contribute a column, it needs to be a subclass of
Tables for models
=================
Most of the time you'll probably be making tables to display queryset data, and
writing such tables involves a lot of duplicate code, e.g.::
If you build use tables to display `.QuerySet` data, rather than defining each
column manually in the table, the `.Table.Meta.model` option allows tables to
be dynamically created based on a model::
>>> class Person(models.Model):
... first_name = models.CharField(max_length=200)
... last_name = models.CharField(max_length=200)
... user = models.ForeignKey("auth.User")
... dob = models.DateField()
...
>>> class PersonTable(tables.Table):
... first_name = tables.Column()
... last_name = tables.Column()
... user = tables.Column()
... dob = tables.Column()
...
# models.py
class Person(models.Model):
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
user = models.ForeignKey("auth.User")
dob = models.DateField()
Often a table will become quite complex after time, e.g. `table.render_foo`_,
changing ``verbose_name`` on columns, or adding an extra
:class:`~.CheckBoxColumn`.
# tables.py
class PersonTable(tables.Table):
class Meta:
model = Person
``django-tables2`` offers the :attr:`.Table.Meta.model` option to ease the pain.
The ``model`` option causes the table automatically generate columns for the
fields in the model. This means that the above table could be re-written as
follows::
This has a number of benefits:
>>> class PersonTable(tables.Table):
... class Meta:
... model = Person
...
>>> PersonTable.base_columns.keys()
['first_name', 'last_name', 'user', 'dob']
- Less code, easier to write, more DRY
- Columns use the field's `~.models.Field.verbose_name`
- Specialised columns are used where possible (e.g. `.DateColumn` for a
`~.models.DateField`)
If you want to customise one of the columns, simply define it the way you would
normally::
When using this approach, the following options are useful:
>>> from django_tables2 import A
>>> class PersonTable(tables.Table):
... user = tables.LinkColumn("admin:auth_user_change", args=[A("user.pk")])
...
... class Meta:
... model = Person
...
>>> PersonTable.base_columns.keys()
['first_name', 'last_name', 'dob', 'user']
It's not immediately obvious but if you look carefully you'll notice that the
order of the fields has now changed -- ``user`` is now last, rather than
``dob``. This follows the same behaviour of Django's model forms, and can be
fixed in a similar way -- the :attr:`.Table.Meta.sequence` option::
>>> class PersonTable(tables.Table):
... user = tables.LinkColumn("admin:auth_user_change", args=[A("user.pk")])
...
... class Meta:
... model = Person
... sequence = ("first_name", "last_name", "user", "dob")
...
>>> PersonTable.base_columns.keys()
['first_name', 'last_name', 'user', 'dob']
… or use a shorter approach that makes use of the special ``"..."`` item::
>>> class PersonTable(tables.Table):
... user = tables.LinkColumn("admin:auth_user_change", args=[A("user.pk")])
...
... class Meta:
... model = Person
... sequence = ("...", "dob")
...
>>> PersonTable.base_columns.keys()
['first_name', 'last_name', 'user', 'dob']
- `~.Table.Meta.sequence` -- reorder columns
- `~.Table.Meta.fields` -- specify model fields to *include*
- `~.Table.Meta.exclude` -- specify model fields to *excluse*
API Reference
=============
:class:`Accessor` (`A`) Objects:
--------------------------------
`.Accessor` (`.A`)
------------------
.. autoclass:: django_tables2.utils.Accessor
:class:`RequestConfig` Objects:
-------------------------------
`.RequestConfig`
----------------
.. autoclass:: django_tables2.config.RequestConfig
:class:`Table` Objects:
-----------------------
`.Table`
--------
.. autoclass:: django_tables2.tables.Table
:members:
:members: paginate, as_html
:class:`Table.Meta` Objects:
----------------------------
`.Table.Meta`
-------------
.. class:: Table.Meta
@ -884,7 +827,7 @@ API Reference
:meth:`.Table.as_html` or the
:ref:`template-tags.render_table` template tag.
:type: ``dict``
:type: `dict`
:default: ``{}``
This is typically used to enable a theme for a table (which is done by
@ -905,10 +848,10 @@ API Reference
Defines the text to display when the table has no rows.
:type: ``string``
:default: ``None``
:type: `unicode`
:default: `None`
If the table is empty and ``bool(empty_text)`` is ``True``, a row is
If the table is empty and ``bool(empty_text)`` is `True`, a row is
displayed containing ``empty_text``. This is allows a message such as
*There are currently no FOO.* to be displayed.
@ -922,7 +865,7 @@ API Reference
Defines which columns should be excluded from the table. This is useful
in subclasses to exclude columns in a parent.
:type: tuple of ``string`` objects
:type: tuple of `unicode`
:default: ``()``
Example::
@ -946,18 +889,41 @@ API Reference
This functionality is also available via the ``exclude`` keyword
argument to a table's constructor.
However, unlike some of the other ``Meta`` options, providing the
However, unlike some of the other `.Table.Meta` options, providing the
``exclude`` keyword to a table's constructor **won't override** the
``Meta.exclude``. Instead, it will be effectively be *added*
`.Meta.exclude`. Instead, it will be effectively be *added*
to it. i.e. you can't use the constructor's ``exclude`` argument to
*undo* an exclusion.
.. attribute:: fields
Used in conjunction with `~.Table.Meta.model`, specifies which fields
should have columns in the table.
:type: tuple of `unicode` or `None`
:default: `None`
If `None`, all fields are used, otherwise only those named.
Example::
# models.py
class Person(models.Model):
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
# tables.py
class PersonTable(tables.Table):
class Meta:
model = Person
fields = ("first_name", )
.. attribute:: model
A model to inspect and automatically create corresponding columns.
:type: Django model
:default: ``None``
:default: `None`
This option allows a Django model to be specified to cause the table to
automatically generate columns that correspond to the fields in a
@ -968,7 +934,7 @@ API Reference
The default ordering. e.g. ``('name', '-age')``. A hyphen ``-`` can be
used to prefix a column name to indicate *descending* order.
:type: ``tuple``
:type: `tuple`
:default: ``()``
.. note::
@ -981,7 +947,7 @@ API Reference
The sequence of the table columns. This allows the default order of
columns (the order they were defined in the Table) to be overridden.
:type: any iterable (e.g. ``tuple`` or ``list``)
:type: any iterable (e.g. `tuple` or `list`)
:default: ``()``
The special item ``"..."`` can be used as a placeholder that will be
@ -1012,15 +978,15 @@ API Reference
.. attribute:: orderable
Default value for column's ``orderable`` attribute.
Default value for column's *orderable* attribute.
:type: ``bool``
:default: ``True``
:type: `bool`
:default: `True`
If the ``Table`` and ``Column`` don't specify a value, a column's
If the table and column don't specify a value, a column's
``orderable`` value will fallback to this. object specify. This
provides an easy mechanism to disable ordering on an entire table,
without adding ``orderable=False`` to each ``Column`` in a ``Table``.
without adding ``orderable=False`` to each column in a table.
.. note::
@ -1031,125 +997,91 @@ API Reference
The default template to use when rendering the table.
:type: ``unicode``
:default: ``django_tables2/table.html``
:type: `unicode`
:default: ``"django_tables2/table.html"``
.. note::
This functionality is also available via the ``template`` keyword
This functionality is also available via the *template* keyword
argument to a table's constructor.
:class:`TableData` Objects:
------------------------------
.. autoclass:: django_tables2.tables.TableData
:members: __init__, order_by, __getitem__, __len__
:class:`BooleanColumn` Objects:
-------------------------------
`.BooleanColumn`
----------------
.. autoclass:: django_tables2.columns.BooleanColumn
:class:`Column` Objects:
------------------------
`.Column`
---------
.. autoclass:: django_tables2.columns.Column
:class:`CheckBoxColumn` Objects:
--------------------------------
`.CheckBoxColumn`
-----------------
.. autoclass:: django_tables2.columns.CheckBoxColumn
:members:
:class:`DateColumn` Objects:
----------------------------
`.DateColumn`
-------------
.. autoclass:: django_tables2.columns.DateColumn
:members:
:class:`DateTimeColumn` Objects:
--------------------------------
`.DateTimeColumn`
-----------------
.. autoclass:: django_tables2.columns.DateTimeColumn
:members:
:class:`EmailColumn` Objects:
-----------------------------
`.EmailColumn`
--------------
.. autoclass:: django_tables2.columns.EmailColumn
:members:
:class:`FileColumn` Objects:
----------------------------
`.FileColumn`
-------------
.. autoclass:: django_tables2.columns.FileColumn
:members:
:class:`LinkColumn` Objects:
----------------------------
`.LinkColumn`
-------------
.. autoclass:: django_tables2.columns.LinkColumn
:members:
:class:`TemplateColumn` Objects:
--------------------------------
`.TemplateColumn`
-----------------
.. autoclass:: django_tables2.columns.TemplateColumn
:members:
:class:`URLColumn` Objects:
--------------------------------
`.URLColumn`
------------
.. autoclass:: django_tables2.columns.URLColumn
:members:
:class:`BoundColumns` Objects
-----------------------------
.. autoclass:: django_tables2.columns.BoundColumns
:members: all, items, orderable, visible, __iter__,
__contains__, __len__, __getitem__
:class:`BoundColumn` Objects
----------------------------
.. autoclass:: django_tables2.columns.BoundColumn
:members:
:class:`BoundRows` Objects
--------------------------
.. autoclass:: django_tables2.rows.BoundRows
:members: __iter__, __len__
:class:`BoundRow` Objects
-------------------------
.. autoclass:: django_tables2.rows.BoundRow
:members: __getitem__, __contains__, __iter__, record, table, items
See :doc:`internal` for internal classes.
Upgrading from django-tables Version 1
======================================
- Change your ``INSTALLLED_APPS`` entry from ``django_tables.app`` to
``django_tables2``.
- Change your ``INSTALLLED_APPS`` entry from ``"django_tables.app"`` to
``"django_tables2"``.
- Change all your import references from ``django_tables`` to
``django_tables2``.
@ -1215,7 +1147,7 @@ Glossary
.. glossary::
accessor
Refers to an :class:`~django_tables2.utils.Accessor` object
Refers to an `.Accessor` object
column name
The name given to a column. In the follow example, the *column name* is
@ -1226,6 +1158,12 @@ Glossary
class SimpleTable(tables.Table):
age = tables.Column()
empty value
An empty value is synonymous with "no value". Columns have an
``empty_values`` attribute that contains values that are considered
empty. It's a way to declare which values from the database correspond
to *null*/*blank*/*missing* etc.
order by alias
A prefixed column name that describes how a column should impact the
order of data within the table. This allows the implementation of how
@ -1251,7 +1189,7 @@ Glossary
A single Python object used as the data for a single row.
render
The act of serialising a :class:`~django_tables2.tables.Table` into
The act of serialising a `.Table` into
HTML.
template
@ -1259,4 +1197,4 @@ Glossary
table data
An interable of :term:`records <record>` that
:class:`~django_tables2.tables.Table` uses to populate its rows.
`.Table` uses to populate its rows.

51
docs/internal.rst Normal file
View File

@ -0,0 +1,51 @@
=============
Internal APIs
=============
The items documented here are internal and subject to change. It exists here
for convenience.
`.BoundColumns`
---------------
.. autoclass:: django_tables2.columns.BoundColumns
:members:
:private-members:
:special-members:
`.BoundColumn`
--------------
.. autoclass:: django_tables2.columns.BoundColumn
:members:
:private-members:
:special-members:
`.BoundRows`
------------
.. autoclass:: django_tables2.rows.BoundRows
:members:
:private-members:
:special-members:
`.BoundRow`
-----------
.. autoclass:: django_tables2.rows.BoundRow
:members:
:private-members:
:special-members:
`.TableData`
------------
.. autoclass:: django_tables2.tables.TableData
:members:
:private-members:
:special-members:

View File

@ -116,6 +116,23 @@ def new_attrs_should_be_supported():
general = Tests()
@general.test
def column_render_supports_kwargs():
class TestColumn(tables.Column):
def render(self, **kwargs):
expected = set(("record", "value", "column", "bound_column",
"bound_row", "table"))
actual = set(kwargs.keys())
assert actual == expected
return "success"
class TestTable(tables.Table):
foo = TestColumn()
table = TestTable([{"foo": "bar"}])
assert table.rows[0]["foo"] == "success"
@general.test
def column_header_should_use_titlised_verbose_name():
class SimpleTable(tables.Table):

12
tox.ini
View File

@ -1,7 +1,8 @@
[testenv]
commands =
python -W error {envbindir}/coverage run setup.py test []
coverage xml --source=django_tables2 -o coverage.xml
coverage html
bash -c 'pylint --reports=no django_tables2 tests > report.pylint || true'
[tools]
testing =
@ -19,12 +20,13 @@ latest = git+https://github.com/django/django/#egg=Django
1.3.x = Django>=1.3,<1.4
1.2.x = Django>=1.2,<1.3
[testenv:pylint]
comamnds =
pylint --files-output=y --reports=no django_tables2 tests
[testenv:docs]
changedir = docs
commands = make html
deps =
{[tools]testing}
pylint
{[django]latest}
Sphinx
; -- python 3.2 ---------------------------------------------------------------