diff --git a/django_tables2/columns/base.py b/django_tables2/columns/base.py index f5edf77f..188138d8 100644 --- a/django_tables2/columns/base.py +++ b/django_tables2/columns/base.py @@ -338,6 +338,26 @@ def header(self): """ return self.verbose_name + @property + def header_value(self): + """ + The value used for the column heading in exports. + + By default this returns `~.Column.header`. + + :returns: `unicode` or `None` + + .. note:: + + This property typically is not accessed directly when a table is + rendered. Instead, `.BoundColumn.header_value` is accessed which + in turn accesses this property. This allows the header to fallback + to the column name (it is only available on a `.BoundColumn` object + hence accessing that first) when this property doesn't return something + useful. + """ + return self.header + def footer(self, bound_column, table): """Return the content of the footer, if specified.""" footer_kwargs = {"column": self, "bound_column": bound_column, "table": table} @@ -556,6 +576,14 @@ def header(self): # fall back to automatic best guess return self.verbose_name + @property + def header_value(self): + """The contents of the header for this column in an export.""" + column_header_value = self.column.header_value + if column_header_value: + return column_header_value + return self.header + @property def footer(self): """The contents of the footer cell for this column.""" diff --git a/django_tables2/tables.py b/django_tables2/tables.py index f7fcded4..5cc6e550 100644 --- a/django_tables2/tables.py +++ b/django_tables2/tables.py @@ -493,7 +493,7 @@ def value_name(self, value): if not (column.column.exclude_from_export or column.name in exclude_columns) ] - yield [force_str(column.header, strings_only=True) for column in columns] + yield [force_str(column.header_value, strings_only=True) for column in columns] for row in self.rows: yield [ diff --git a/docs/pages/custom-data.rst b/docs/pages/custom-data.rst index adf36c07..565dc764 100644 --- a/docs/pages/custom-data.rst +++ b/docs/pages/custom-data.rst @@ -151,9 +151,9 @@ Please refer to `.Table.as_values` for an example. 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:: +Defining a column subclass allows further customization and 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 >>> @@ -186,3 +186,19 @@ For complicated columns, you may want to return HTML from the ... def render(self, value): ... return format_html('', value) ... + +When subclassing a column, the header in the html table can be customized by +overriding :meth:`~Column.header`. The header value for exports can be independently +customized using :meth:`~Column.render_value`. + + + >>> from django.utils.html import format_html + >>> + >>> class PartyColumn(tables.Column): + ... @property + ... def header(self): + ... return format_html('
') + ... + ... @property + ... def header_value(self): + ... return "party" diff --git a/docs/pages/export.rst b/docs/pages/export.rst index 8a12567e..c1835b69 100644 --- a/docs/pages/export.rst +++ b/docs/pages/export.rst @@ -80,6 +80,10 @@ By default, it just calls the `render()` method on that column. If your custom column produces HTML, you should override this method and return the actual value. +For column headers, similarly sometimes :ref:Column.header`-may include html which +would be inappropriate to include in an export. For this case, the `:ref:Column.header_value` +can be used to override the column header only in exports. + Including and excluding columns ------------------------------- diff --git a/tests/test_core.py b/tests/test_core.py index d7c64d02..15286158 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -661,6 +661,22 @@ def value_country(self, value): self.assertEqual(list(Table(self.AS_VALUES_DATA).as_values()), expected) + def test_as_value_header_value(self): + class CustomColumn(tables.Column): + + @property + def header_value(self): + return "CUSTOM" + + class Table(tables.Table): + name = tables.Column() + country = CustomColumn() + + expected = [["Name", "CUSTOM"]] + [[r["name"], r["country"]] for r in self.AS_VALUES_DATA] + table = Table(self.AS_VALUES_DATA) + + self.assertEqual(list(table.as_values()), expected) + def test_as_values_accessor_relation(self): programmer = Occupation.objects.create(name="Programmer") henk = Person.objects.create(