1 # -*- coding: utf-8 -*-
2 from django.utils.safestring import EscapeUnicode, SafeData
3 from .proxies import TemplateSafeLazyProxy
7 class BoundRow(object):
9 Represents a *specific* row in a table.
11 :class:`.BoundRow` objects are a container that make it easy to access the
12 final 'rendered' values for cells in a row. You can simply iterate over a
13 :class:`.BoundRow` object and it will take care to return values rendered
14 using the correct method (e.g. :meth:`.Column.render_FOO`)
16 To access the rendered value of each cell in a row, just iterate over it:
18 .. code-block:: python
20 >>> import django_tables as tables
21 >>> class SimpleTable(tables.Table):
22 ... a = tables.Column()
23 ... b = tables.CheckBoxColumn(attrs={'name': 'my_chkbox'})
25 >>> table = SimpleTable([{'a': 1, 'b': 2}])
26 >>> row = table.rows[0] # we only have one row, so let's use it
31 <input type="checkbox" name="my_chkbox" value="2" />
33 Alternatively you can treat it like a list and use indexing to retrieve a
34 specific cell. It should be noted that this will raise an IndexError on
37 .. code-block:: python
42 u'<input type="checkbox" name="my_chkbox" value="2" />'
45 IndexError: list index out of range
47 Finally you can also treat it like a dictionary and use column names as the
48 keys. This will raise KeyError on failure (unlike the above indexing using
51 .. code-block:: python
56 u'<input type="checkbox" name="my_chkbox" value="2" />'
61 :param table: is the :class:`Table` in which this row exists.
62 :param record: a single record from the :term:`table data` that is used to
63 populate the row. A record could be a :class:`Model` object, a
64 :class:`dict`, or something else.
67 def __init__(self, table, record):
73 """The associated :class:`.Table` object."""
79 The data record from the data source which is used to populate this row
87 Iterate over the rendered values for cells in the row.
89 Under the hood this method just makes a call to
90 :meth:`.BoundRow.__getitem__` for each cell.
93 for column in self.table.columns:
94 # this uses __getitem__, using the name (rather than the accessor)
95 # is correct – it's what __getitem__ expects.
96 yield self[column.name]
98 def __getitem__(self, name):
100 Returns the final rendered value for a cell in the row, given the name
104 bound_column = self.table.columns[name]
108 raw = bound_column.accessor.resolve(self.record)
109 except (TypeError, AttributeError, KeyError, ValueError) as e:
111 return raw if raw is not None else bound_column.default
114 'value': TemplateSafeLazyProxy(value),
115 'record': self.record,
116 'column': bound_column.column,
117 'bound_column': bound_column,
119 'table': self._table,
121 render_FOO = 'render_' + bound_column.name
122 render = getattr(self.table, render_FOO, bound_column.column.render)
124 return render(**kwargs)
125 except TypeError as e:
126 # Let's be helpful and provide a decent error message, since
127 # render() underwent backwards incompatible changes.
128 if e.message.startswith('render() got an unexpected keyword'):
129 if hasattr(self.table, render_FOO):
130 cls = self.table.__class__.__name__
133 cls = kwargs['column'].__class__.__name__
135 msg = 'Did you forget to add **kwargs to %s.%s() ?' % (cls, meth)
136 raise TypeError(e.message + '. ' + msg)
138 def __contains__(self, item):
139 """Check by both row object and column name."""
140 if isinstance(item, basestring):
141 return item in self.table._columns
146 class BoundRows(object):
148 Container for spawning :class:`.BoundRow` objects.
150 The :attr:`.Table.rows` attribute is a :class:`.BoundRows` object.
151 It provides functionality that would not be possible with a simple iterator
154 :type table: :class:`.Table` object
155 :param table: the table in which the rows exist.
158 def __init__(self, table):
163 Return an iterable for all :class:`.BoundRow` objects in the table.
166 for record in self.table.data:
167 yield BoundRow(self.table, record)
171 If the table is paginated, return an iterable of :class:`.BoundRow`
172 objects that appear on the current page.
174 :rtype: iterable of :class:`.BoundRow` objects, or :const:`None`.
176 if not hasattr(self.table, 'page'):
178 return iter(self.table.page.object_list)
181 """Convience method for :meth:`.BoundRows.all`"""
185 """Returns the number of rows in the table."""
186 return len(self.table.data)
188 # for compatibility with QuerySetPaginator
191 def __getitem__(self, key):
192 """Allows normal list slicing syntax to be used."""
193 if isinstance(key, slice):
194 return itertools.imap(lambda record: BoundRow(self.table, record),
195 self.table.data[key])
196 elif isinstance(key, int):
197 return BoundRow(self.table, self.table.data[key])
199 raise TypeError('Key must be a slice or integer.')