File: custom-data.rst

package info (click to toggle)
django-tables 2.7.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,752 kB
  • sloc: python: 7,120; makefile: 132; sh: 74
file content (188 lines) | stat: -rw-r--r-- 6,545 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
.. _accessors:

Alternative column data
=======================

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.


Using `~.Accessors`
-------------------

Each column has a 'key' that describes which value to pull from each record to
populate the column's cells. By default, this key is just the name given to the
column, but it can be changed to allow foreign key traversal or other complex
cases.

To reduce ambiguity, rather than calling it a 'key', we use the name 'accessor'.

Accessors are just double-underscore separated paths that describe how an object
should be traversed to reach a specific value, for example::

    >>> from django_tables2 import A
    >>> data = {"abc": {"one": {"two": "three"}}}
    >>> A("abc__one__two").resolve(data)
    'three'

The separators ``__`` represent relationships, and are attempted in this order:

1. Dictionary lookup ``a[b]``
2. Attribute lookup ``a.b``
3. List index lookup ``a[int(b)]``

If the resulting value is callable, it is called and the return value is used.

Moreover, you can use `accessor` with the table columns to access nested values. For example::

    >>> data = [{"abc": {"one": {"two": "three"}}}, {"abc": {"one": {"two": "four"}}}]
    >>> class MyTable(tables.Table):
    ...    abc = tables.Column(accessor="abc__one__two")
    >>> table = MyTable(data)
    >>> list(map(str, table.rows[1]))
    ['four']

.. _table.render_foo:

`Table.render_foo` methods
--------------------------

To change how a column is rendered, define a ``render_foo`` method on
the table for example: `render_row_number()` for a column named `row_number`.
This approach is suitable if you have a one-off change that you do not want to
use in multiple tables or if you want to combine the data from two columns into one.

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``

This example shows how to render the row number in the first row::

    >>> 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().__init__(*args, **kwargs)
    ...         self.counter = itertools.count()
    ...
    ...     def render_row_number(self):
    ...         return f"Row {next(self.counter)}"
    ...
    ...     def render_id(self, value):
    ...         return f"<{value}>"
    ...
    >>> table = SimpleTable([{"age": 31, "id": 10}, {"age": 34, "id": 11}])
    >>> print(", ".join(map(str, table.rows[0])))
    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.

The `render_foo` method can also be used to combine data from two columns into one column.
The following example shows how the the value for the `last_name` field is appended to the
`name` field using the `render_name` function.
Note that `value` is the value in the column and `record` is used to access the values in
the `last_name` column::

    # models.py
    class Customers(models.Model):
        name = models.CharField(max_length=50, null=False, blank=False)
        last_name = models.CharField(max_length=50, null=False, blank=False)
        description = models.TextField(blank=True)

    # tables.py
    from .models import Customers
    from django.utils.html import format_html

    class CustomerTable(tables.Table):
        name = tables.Column()
        description = tables.Column()

        def render_name(self, value, record):
            return format_html("<b>{} {}</b>", value, record.last_name)

If you need to access logged-in user (or request in general) in your render methods, you can reach it through
`self.request`::

    def render_count(self, value):
        if self.request.user.is_authenticated():
            return value
        else:
            return '---'

.. important::

    `render_foo` 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).

.. important::

    `render_foo` methods determine what value is rendered, but which make sorting the
    column have unexpected results. In those cases, you might want to also define a
    :ref:`table.order_foo` method.

.. _table.value_foo:

`Table.value_foo` methods
-------------------------

If you want to use `Table.as_values` to export your data, you might want to define
a method ``value_foo``, which is analogous to ``render_foo``, but used to render the
values rather than the HTML output.

Please refer to `.Table.as_values` for an example.

.. _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)
    >>> # renders to something like this:
    '''<table>
        <thead><tr><th>Normal</th><th>Upper</th></tr></thead>
        <tbody><tr><td>Hi there!</td><td>HI THERE!</td></tr></tbody>
    </table>'''

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. Make sure to use Django's html formatting functions::

    >>> from django.utils.html import format_html
    >>>
    >>> class ImageColumn(tables.Column):
    ...     def render(self, value):
    ...         return format_html('<img src="/media/img/{}.jpg" />', value)
    ...