0
votes

Using django-tables2 has been a great pleasure. I'm now trying to figure out how to alter css for rows, based on the row content. For example, given the table:

header 1 | header 2
---------|---------
value 1  | 1
value 2  | 1
value 3  | 2
value 4  | 2
value 5  | 2
value 6  | 3
value 7  | 3

I'd like to be able to style sequential rows with identical 'header 2' values with the same class. With itertools.groupby, it's easy enough to group the values that should be grouped, but i can't figure out how to move through the table object and update the class attrs for each row.

For columns, one can dynamically update the attrs. For example, in init, self.columns['ColumnName'].column.attrs = {'td': {'class': 'foo'}} works. However, for rows, this (and various permutations) does not work:

for i in self.rows:
    i.attrs = {'tr': {'class': 'foo'}}  # *** AttributeError, can't set attribute
    i.cells.row.attrs = {...}           # *** AttributeError
    i.cells.row.attrs['class'] = 'foo'  # Seems to work, but attrs not updated
    # same as above
    i.cells.row.attrs.update({'tr': {'class': 'foo'}})
    i.table.row_attrs = {'class': 'foo'} # works, but applies to the whole table at once.

Clearly i have the wrong end of the stick here. The documentation suggests using row_attrs in class Meta and that can have a function to return the class value. However, in that function, one can only see one row at a time (passed as the 'record' object), whereas in this case one needs to parse the table as a whole, so init seems like a better place to address this from.

A solution to this sticky situation or any pointers would be much appreciated.

1
I still think you should use row_attrs, with a callable. But it will need to keep some state, so a simple lambda won't do. Why do you need to 'parse the whole table'? - Jieter
@Jieter, thanks so much for your feedback. I may not understand how to use row_attrs properly. I assume you mean in the Meta class as indicated in the docs. If i use it with a callable, as far as i can tell, i only have access to the values of the current row, but in order to work out the grouping (which i want to use to change the bg color), i need a way to know when the current row has a new value compared to the previous row, and then change to a different css class. Their are many data values, but i just want to use two bg colors that change intermittently whenever the data value changes. - fzzylogic

1 Answers

1
votes

If you want to access the previous rows, you can use a callable returning a callable like this:

def group_by_heading():
    class_names = ["red", "blue", "yellow", ...]
    previous = None

    def inner(record):
        # in first row and after every change, remove the first element
        # and use it as the current class name.
        if previous is None or record.header_2 != previous.header_2:
            class_name = class_names.pop(0)

        previous = record
        return class_name
    return inner


class Table(tables.Table):
    class Meta:
        row_attrs = {'td': {'class': group_by_heading()}}

Note that the function group_by_heading is called when adding it to the row_attrs dict. This is called a closure

Note that this is not a complete example. If applied as is, the list of class_names will be quickly exhaustedbecause it is re-used every time the table is rendered. You can fix that by adding the row_attrs to the table in your view. For example, using SingleTableView:

class MyView(tables.SingleTableView):
    table_class = MyTable
    queryset = Model.objects.all()

    def get_table_kwargs(self):
        return {"row_attrs": {'td': {'class': group_by_heading()}}}

This will ensure a fresh list of class_names is used every table view.