1 # -*- coding: utf-8 -*-
2 from itertools import imap, ifilter
4 from django.utils.safestring import EscapeUnicode, SafeData
5 from django.utils.functional import curry
8 class BoundRow(object):
10 Represents a *specific* row in a table.
12 :class:`.BoundRow` objects are a container that make it easy to access the
13 final 'rendered' values for cells in a row. You can simply iterate over a
14 :class:`.BoundRow` object and it will take care to return values rendered
15 using the correct method (e.g. :meth:`.Column.render_FOO`)
17 To access the rendered value of each cell in a row, just iterate over it:
19 .. code-block:: python
21 >>> import django_tables2 as tables
22 >>> class SimpleTable(tables.Table):
23 ... a = tables.Column()
24 ... b = tables.CheckBoxColumn(attrs={'name': 'my_chkbox'})
26 >>> table = SimpleTable([{'a': 1, 'b': 2}])
27 >>> row = table.rows[0] # we only have one row, so let's use it
32 <input type="checkbox" name="my_chkbox" value="2" />
34 Alternatively you can treat it like a list and use indexing to retrieve a
35 specific cell. It should be noted that this will raise an IndexError on
38 .. code-block:: python
43 u'<input type="checkbox" name="my_chkbox" value="2" />'
46 IndexError: list index out of range
48 Finally you can also treat it like a dictionary and use column names as the
49 keys. This will raise KeyError on failure (unlike the above indexing using
52 .. code-block:: python
57 u'<input type="checkbox" name="my_chkbox" value="2" />'
62 :param table: is the :class:`Table` in which this row exists.
63 :param record: a single record from the :term:`table data` that is used to
64 populate the row. A record could be a :class:`Model` object, a
65 :class:`dict`, or something else.
68 def __init__(self, table, record):
74 """The associated :class:`.Table` object."""
80 The data record from the data source which is used to populate this row
88 Iterate over the rendered values for cells in the row.
90 Under the hood this method just makes a call to
91 :meth:`.BoundRow.__getitem__` for each cell.
94 for column in self.table.columns:
95 # this uses __getitem__, using the name (rather than the accessor)
96 # is correct – it's what __getitem__ expects.
97 yield self[column.name]
99 def __getitem__(self, name):
101 Returns the final rendered value for a cell in the row, given the name
105 bound_column = self.table.columns[name]
109 raw = bound_column.accessor.resolve(self.record)
110 except (TypeError, AttributeError, KeyError, ValueError) as e:
112 return raw if raw is not None else bound_column.default
115 'value': value, # already a function
116 'record': lambda: self.record,
117 'column': lambda: bound_column.column,
118 'bound_column': lambda: bound_column,
119 'bound_row': lambda: self,
120 'table': lambda: self._table,
122 render_FOO = 'render_' + bound_column.name
123 render = getattr(self.table, render_FOO, bound_column.column.render)
125 # just give a list of all available methods
126 funcs = ifilter(curry(hasattr, inspect), ('getfullargspec', 'getargspec'))
127 spec = getattr(inspect, next(funcs))
128 # only provide the arguments that the func is interested in
130 for name in spec(render).args:
133 kw[name] = kwargs[name]()
136 def __contains__(self, item):
137 """Check by both row object and column name."""
138 if isinstance(item, basestring):
139 return item in self.table._columns
144 class BoundRows(object):
146 Container for spawning :class:`.BoundRow` objects.
148 The :attr:`.Table.rows` attribute is a :class:`.BoundRows` object.
149 It provides functionality that would not be possible with a simple iterator
152 :type table: :class:`.Table` object
153 :param table: the table in which the rows exist.
156 def __init__(self, table):
160 """Convience method for :meth:`.BoundRows.all`"""
161 for record in self.table.data:
162 yield BoundRow(self.table, record)
165 """Returns the number of rows in the table."""
166 return len(self.table.data)
168 # for compatibility with QuerySetPaginator
171 def __getitem__(self, key):
172 """Allows normal list slicing syntax to be used."""
173 if isinstance(key, slice):
174 return imap(lambda record: BoundRow(self.table, record),
175 self.table.data[key])
176 elif isinstance(key, int):
177 return BoundRow(self.table, self.table.data[key])
179 raise TypeError('Key must be a slice or integer.')