Source code for django_tables2.rows
# coding: utf-8
from django.db import models
from django.db.models.fields import FieldDoesNotExist
from django.utils import six
from .utils import A, getargspec
[docs]class BoundRow(object):
"""
Represents a *specific* row in a table.
`.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
`.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:
.. code-block:: python
>>> import django_tables2 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'
: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 `~django.db.Model`
object, a `dict`, or something else.
"""
def __init__(self, record, table):
self._record = record
self._table = table
@property
def table(self):
"""The associated `.Table` object."""
return self._table
@property
def record(self):
"""
The data record from the data source which is used to populate this row
with data.
"""
return self._record
[docs] def __iter__(self):
"""
Iterate over the rendered values for cells in the row.
Under the hood this method just makes a call to
`.BoundRow.__getitem__` for each cell.
"""
for column, value in self.items():
# this uses __getitem__, using the name (rather than the accessor)
# is correct – it's what __getitem__ expects.
yield value
[docs] def __getitem__(self, name):
"""
Returns the final rendered value for a cell in the row, given the name
of a column.
"""
bound_column = self.table.columns[name]
value = None
# We need to take special care here to allow get_FOO_display()
# methods on a model to be used if available. See issue #30.
path, _, remainder = bound_column.accessor.rpartition('.')
penultimate = A(path).resolve(self.record, quiet=True)
# If the penultimate is a model and the remainder is a field
# using choices, use get_FOO_display().
if isinstance(penultimate, models.Model):
try:
field = penultimate._meta.get_field(remainder)
display = getattr(penultimate, 'get_%s_display' % remainder, None)
if getattr(field, "choices", ()) and display:
value = display()
remainder = None
except FieldDoesNotExist:
pass
# Fall back to just using the original accessor (we just need
# to follow the remainder).
if remainder:
value = A(remainder).resolve(penultimate, quiet=True)
if value in bound_column.column.empty_values:
return bound_column.default
available = {
'value': value,
'record': self.record,
'column': bound_column.column,
'bound_column': bound_column,
'bound_row': self,
'table': self._table,
}
expected = {}
# provide only the arguments expected by `render`
argspec = getargspec(bound_column.render)
args, varkw = argspec[0], argspec[2]
if varkw:
expected = available
else:
for key, value in available.items():
if key in args[1:]:
expected[key] = value
return bound_column.render(**expected)
[docs] def __contains__(self, item):
"""Check by both row object and column name."""
if isinstance(item, six.string_types):
return item in self.table.columns
else:
return item in self
[docs] def items(self):
"""
Returns iterator yielding ``(bound_column, cell)`` pairs.
*cell* is ``row[name]`` -- the rendered unicode value that should be
``rendered within ``<td>``.
"""
for column in self.table.columns:
yield (column, self[column.name])
[docs]class BoundRows(object):
"""
Container for spawning `.BoundRow` objects.
:param data: iterable of records
:param table: the table in which the rows exist
This is used for `.Table.rows`.
"""
def __init__(self, data, table):
self.data = data
self.table = table
def __iter__(self):
for record in self.data:
yield BoundRow(record, table=self.table)
def __len__(self):
return len(self.data)
[docs] def __getitem__(self, key):
"""
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)