Skip to content

Commit

Permalink
Fix #499 - add default "class" HTML attribute to footers (#501)
Browse files Browse the repository at this point in the history
* _report is _export, really

* Add BeautifulSoup4 dependency, as some tests are going to need it because of more and more complicated HTML generated by dt2

* Switch to BeautifulSoup4 so when the fix for #499 comes, we will be analysing HTML and not just comparing simple strings.

* Add a failing test for footer columns without „class” attribute

* A typo

* Add support for footer attributes, especially the „class” attribute.

* isort

* Document the changes

* Default row styling, style, typos

* Describe tf attribute in Column.attrs

* Remove double space

* Add cross-refs from css to column-attributes

* Don't use BeautifulSoup4 for tests, use lxml which is being used already

* isort

* isort, again
  • Loading branch information
mpasternak authored and jieter committed Nov 27, 2017
1 parent 5e8cc7e commit a414185
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 21 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
## 1.15.0 (2017-11-23)
- Added `as=varname` keyword argument to the `{% querystring %}` template tag,
fixes [#481](https://github.com/jieter/django-tables2/issues/481)
- Updated the tutorial to reflect current state of Django a bit better.
- Updated the tutorial to reflect current state of Django a bit better
- Used `OrderedDict` rather than `dict` as the parent for `utils.AttributeDict` to make the rendered html more consistant accross python versions.
- Allow reading column `attrs` from a column's attribute, allowing easier reuse of custom column attributes (fixes [#241](https://github.com/jieter/django-tables2/issues/241))
- `value` and `record` are optionally passed to the column attrs callables for data rows. [#503](https://github.com/jieter/django-tables2/pull/503), fixes [#500](https://github.com/jieter/django-tables2/issues/500)
- Added `tf` dictionary to `Column.attrs` with default values for the footer, so footers now have `class` attribute by default

## 1.14.2 (2017-10-30)
- Added a `row_counter` variable to the template context in `TemplateColumn` (fixes [#448](https://github.com/jieter/django-tables2/issues/488))
Expand Down
12 changes: 8 additions & 4 deletions django_tables2/columns/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class Column(object):
the data iterator returned from as_values().
footer (str, callable): Defines the footer of this column. If a callable
is passed, it can take optional keyword argumetns `column`,
`bound_colun` and `table`.
`bound_column` and `table`.
order_by (str, tuple or `.Accessor`): Allows one or more accessors to be
used for ordering rather than *accessor*.
orderable (bool): If `False`, this column will not be allowed to
Expand Down Expand Up @@ -298,9 +298,10 @@ def attrs(self):
'''
Proxy to `.Column.attrs` but injects some values of our own.
A ``th`` and ``td`` are guaranteed to be defined (irrespective of
what's actually defined in the column attrs. This makes writing
templates easier.
A ``th``, ``td`` and ``tf`` are guaranteed to be defined (irrespective
of what's actually defined in the column attrs. This makes writing
templates easier. ``tf`` is not actually a HTML tag, but this key name
will be used for attributes for column's footer, if the column has one.
'''

# prepare kwargs for computed_values()
Expand All @@ -327,14 +328,17 @@ def attrs(self):
# override with attrs defined specifically for th and td respectively.
attrs['th'] = computed_values(attrs.get('th', cell_attrs), kwargs=kwargs)
attrs['td'] = computed_values(attrs.get('td', cell_attrs), kwargs=kwargs)
attrs['tf'] = computed_values(attrs.get('tf', cell_attrs), kwargs=kwargs)

# wrap in AttributeDict
attrs['th'] = AttributeDict(attrs['th'])
attrs['td'] = AttributeDict(attrs['td'])
attrs['tf'] = AttributeDict(attrs['tf'])

# Override/add classes
attrs['th']['class'] = self.get_th_class(attrs['th'])
attrs['td']['class'] = self.get_td_class(attrs['td'])
attrs['tf']['class'] = self.get_td_class(attrs['tf'])

return attrs

Expand Down
2 changes: 1 addition & 1 deletion django_tables2/templates/django_tables2/semantic.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
{% if table.has_footer %}
<tr>
{% for column in table.columns %}
<td>{{ column.footer }}</td>
<td {{ column.attrs.tf.as_html }}>{{ column.footer }}</td>
{% endfor %}
</tr>
{% endif %}
Expand Down
2 changes: 1 addition & 1 deletion django_tables2/templates/django_tables2/table.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
<tfoot>
<tr>
{% for column in table.columns %}
<td>{{ column.footer }}</td>
<td {{ column.attrs.tf.as_html }}>{{ column.footer }}</td>
{% endfor %}
</tr>
</tfoot>
Expand Down
7 changes: 5 additions & 2 deletions docs/pages/column-attributes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ Depending on the column, different elements are supported, however ``th``,


For ``th`` and ``td``, the column name will be added as a class name. This makes
selecting the row for styling easier.
Have a look at each column's API reference to find which elements are supported.
selecting the row for styling easier. Have a look at each column's API
reference to find which elements are supported.

If you need to add some extra attributes to column's tags rendered in the
footer, use key name ``tf``, as described in section on :ref:`css`.

Callables passed in this dict will be called, with optional kwargs ``table``,
``bound_column`` ``record`` and ``value``, with the return value added. For example::
Expand Down
38 changes: 38 additions & 0 deletions docs/pages/custom-rendering.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,44 @@ a hook that allows arbitrary attributes to be added to the ``<table>`` tag.
>>> # renders to something like this:
'<table class="mytable">...'

Also every column gets a class attribute, which by default is the same as the
column's label. Also, by default, odd rows' class is ``odd`` and even rows'
class is ``even``. So rows of the ``SimpleTable()`` from previous example
in django-tables2 default configuration will look like:

.. sourcecode:: html

<tr class="odd">
<td class="id">...</td>
<td class="age">...</td>
</tr>
<tr class="even">
<td class="id">...</td>
<td class="age">...</td>
</tr>

You can also specify ``attrs`` attribute when creating a column. ``attrs``
is a dictionary which contains attributes which by default get rendered
on various tags involved with rendering a column. You can read more about
them in :ref:`column-attributes`. django-tables2 supports 3 different
dictionaries, this way you can give different attributes
to column tags in table header (``th``), rows (``td``) or footer (``tf``)

.. sourcecode:: python

>>> import django_tables2 as tables
>>>
>>> class SimpleTable(tables.Table):
... id = tables.Column(attrs={'td': {'class': 'my-class'}})
... age = tables.Column(attrs={'tf': {'bgcolor': 'red'}})
...
>>> table = SimpleTable()
>>> # renders to something like this:
'<tbody><tr><td class="my-class">...</td></tr>'
>>> # and the footer will look like this:
'<tfoot><tr> ... <td class="age" bgcolor="red"></tr></tfoot>''


.. _custom-template:

Custom Template
Expand Down
2 changes: 1 addition & 1 deletion requirements/common.pip
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ pytz>0
pytest
pytest-django
pytest-cov
tablib==0.11.4
tablib==0.11.4
5 changes: 3 additions & 2 deletions tests/test_faq.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import django_tables2 as tables

from .utils import build_request
from .utils import build_request, parse

TEST_DATA = [
{'name': 'Belgium', 'population': 11200000},
Expand Down Expand Up @@ -42,4 +42,5 @@ class CountryTable(tables.Table):
table = CountryTable(TEST_DATA)
html = table.as_html(build_request())

assert '<td>Total: 77740000</td>' in html
columns = parse(html).findall(".//tfoot/tr")[-1].findall("td")
assert columns[1].text == "Total: 77740000"
57 changes: 48 additions & 9 deletions tests/test_footer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# coding: utf-8
import django_tables2 as tables

from .utils import build_request
from .utils import build_request, parse

MEMORY_DATA = [
{'name': 'Queensland', 'country': 'Australia', 'population': 4750500},
Expand Down Expand Up @@ -31,11 +31,11 @@ class Table(tables.Table):

table = Table(MEMORY_DATA)
assert table.has_footer() is True

html = table.as_html(build_request('/'))

assert '<td>Total:</td>' in html
assert '<td>18833000</td>' in html
columns = parse(html).findall(".//tfoot/tr")[-1].findall("td")
assert columns[1].text == "Total:"
assert columns[2].text == "18833000"


def test_footer_disable_on_table():
Expand All @@ -52,17 +52,56 @@ class Table(tables.Table):


def test_footer_column_method():
class SummingColumn(tables.Column):
def render_footer(self, bound_column, table):
return sum(
bound_column.accessor.resolve(row) for row in table.data)

class TestTable(tables.Table):
name = tables.Column()
country = tables.Column(footer='Total:')
population = SummingColumn()

table = TestTable(MEMORY_DATA)
html = table.as_html(build_request('/'))

columns = parse(html).findall(".//tfoot/tr")[-1].findall("td")
assert columns[1].text == "Total:"
assert columns[2].text == "18833000"


def test_footer_has_class():
class SummingColumn(tables.Column):
def render_footer(self, bound_column, table):
return sum(bound_column.accessor.resolve(row) for row in table.data)
return sum(
bound_column.accessor.resolve(row) for row in table.data)

class Table(tables.Table):
class TestTable(tables.Table):
name = tables.Column()
country = tables.Column(footer='Total:')
population = SummingColumn()

table = Table(MEMORY_DATA)
table = TestTable(MEMORY_DATA)
html = table.as_html(build_request('/'))

columns = parse(html).findall(".//tfoot/tr")[-1].findall("td")
assert "class" in columns[1].attrib


def test_footer_custom_attriubtes():
class SummingColumn(tables.Column):
def render_footer(self, bound_column, table):
return sum(
bound_column.accessor.resolve(row) for row in table.data)

class TestTable(tables.Table):
name = tables.Column()
country = tables.Column(footer='Total:', attrs={'tf': {'align': 'right'}})
population = SummingColumn()

table = TestTable(MEMORY_DATA)
table.columns['country'].attrs['tf'] = {'align': 'right'}
html = table.as_html(build_request('/'))
assert '<td>Total:</td>' in html
assert '<td>18833000</td>' in html

columns = parse(html).findall(".//tfoot/tr")[-1].findall("td")
assert "align" in columns[1].attrib

0 comments on commit a414185

Please sign in to comment.