Hello,

I wrote a version of DataGrid that supports "dynamic" columns, e.g. a column 
which value depends on the current row being processed. I wonder if it's useful 
enough to be shared and may be integrated with stock DataGrid (it is fully 
backward compatible).

# Usage is like this:
grid = DataGrid(fields=['userId', ('Homepage', get_user_home_url)])

there is tuple instead of plain string; the second arg is callable which is 
called for each raw (sql object). E.g:

def get_user_home_url(user):
  from kid.pull import XML
  s = '<a href="%s">%s</a>' % (user.url, user.url)
  return XML(s)

Here is implementation:

(the template is the same, the only difference is ${widget.getcol(row, col)} 
instead of getattr)


class DataGrid(widgets.Widget):
    """Presents a grid with add/edit/delete controls based on a
     SelectResults object."""
    css=[widgets.CSSLink(widgets.static, "grid.css")]
    template = """
    <div xmlns:py="http://purl.org/kid/ns#";>
    <table id="${widget.name}" class="grid" cellpadding="0" cellspacing="1" 
border="0">
        <thead py:if="headers">
            <td>&nbsp;</td>
            <td py:for="head in headers">
                ${head}
            </td>
        </thead>
        <tr py:for="i, row in enumerate(widget_value)" class="${i%2 and 'odd' 
or 'even'}">
            <td>
                <a href="${std.url([str(row.id), 'edit'])}">
                <img src="${std.tg_static}/images/edit.png" border="0"/>
                </a>
                <a href="${std.url([str(row.id), 'delete'])}">
                <img src="${std.tg_static}/images/discard.png" border="0"/>
                </a>
            </td>
            <td py:for="col in collist">
              ${widget.getcol(row, col)}
            </td>
        </tr>
    </table>
    <p><a href="add" style="text-decoration:none"><img 
src="${std.tg_static}/images/add.png" border="0"/> Add a record</a></p>
    </div>
    """

    def __init__(self, fields=None, **kw):
        super(DataGrid, self).__init__(**kw)
        self.fields = fields
        self.dynfields = {}
        self.collist = []

    def getcol(self, row, col):
        if col in self.dynfields:
            return self.dynfields[col](row)
        return getattr(row, col)

    def update_dict(self, d):
        "Sets up headers based on the select results columns"
        value = d["widget_value"]
        cols = value.sourceClass.sqlmeta.columns
        collist_raw = d.get('fields') or self.fields or list(cols.keys())
        headers = []
        collist = []
        for name in collist_raw:
            if not isinstance(name, tuple):
                column = cols[name]
                header = column.title or column.name.capitalize()
            else:
                header, f = name
                self.dynfields[header] = f
                name = header # to be able to key dynfields dict
            headers.append(header)
            collist.append(name)
        d.update(dict(headers=headers, collist=collist))


Reply via email to