wiki:DataGridWidget
Warning: Can't synchronize with repository "(default)" (Unsupported version control system "svn": No module named svn). Look in the Trac log for more information.

Version 8 (modified by nyenyec, 9 years ago) (diff)

minor typos

Overview

DataGrid helps you to present your data on a page in a tabular form. DataGrid's purpose is to render a list of same-kind objects (such as a list of your model instances) in a nice, tabular layout with minimal efforts. The DataGrid subclass, FastDataGrid?, integrates nicely with fastdata controllers by providing inline links to edit or delete an object or create new object of the same type.

Differences between DataGrid and FastDataGrid

DataGrid requires you to describe what you're going to display via fields parameter at construction time. FastDataGrid? is sophisticated enough to figure out how to display an arbitrary SelectResults? instance (this is what you get when you do .select() on you SQLObject's model object).

They also have different Kid templates. DataGrid's template is very simple and on purpose -- the goal is to be easy to grasp and easy to extend/replace with a custom one. On the other hand, the FastData? template is much more sophisticated, designed to work within FastData? environment. It is intended to be used "as is", giving the user certain hooks to customize its appearance.

Basic DataGrid usage

Suppose you want to display a list of Users in your system. Here is a sample User declaration (note that it's not an SQLObject -- just a plain Python class):

class User:
    def __init__(self, ID, name, emailAddress):
        self.userId = ID
        self.name = name
        self.emailAddress = emailAddress
    displayName = property(fget=lambda self: self.name.capitalize())

Given the above definition, you may want to display Users in the following form:

from turbogears.widgets import DataGrid

users_admin_form = DataGrid(fields=[
    ('ID', 'userId'),
    ('Name', 'displayName'),
    ('E-mail', 'emailAddress'),
])

As you have probably guessed, fields parameter defines what and how would be displayed by this DataGrid instance. Each tuple defines single column in the resulting table; first tuple's element defines column's title while second element defines accessor. Accessor usually refers to attribute's name, such as User.userId or property's name, such as User.displayName of our model class.

To display a users' table you pass a list of Users to the users_admin_form.display() method:

users = [User(1, 'john', 'john@foo.net'), User(2, 'fred', 'fred@foo.net')]
users_admin_form.display(users)

Result will look something like this (just a bit prettier):

IDNameE-mail
1Johnjohn@foo.net
2Fredfred@foo.net

Fastdata integration

For a fastdata environment you're mostly likely to use FastDataGrid?:

from turbogears.fastdata import DataController
from turbogears.fastdata.datawidgets import FastDataGrid
from model import TG_User

class Root(controllers.RootController):
    users = DataController(sql_class=TG_User, list_widget=users_admin_form)

Now if you access /users/ page you should see a table with a list of users, along with edit/delete icons and "Add a record" link at the bottom.

Note that unlike plain DataGrid class you don't need to pass fields parameter to FastDataGrid?. Instead, FastDataGrid? able to inspect SelectResults? instance it gets to extract SQLObject's metadata. In other words, FastDataGrid? instance can easily render arbitrary SQLObjects while with DataGrid you need to list result fields explicitly. You may still want to pass fields parameter to FastDataGrid? if you want to customize fields' representation: fields, their order and how they are displayed.

class Root(controllers.RootController):
    users = DataController(sql_class=TG_User,
        list_widget=users_admin_form,
        list_template='.templates.admin_users')

Add something like this to your admin_users template:

${list_widget.display(data, show_actions=False, add_link_title='Add User')}

Customizing FastDataGrid representation

You can tweak FastDataGrid?'s representation to a certain extent by passing configuration options at "display time" (when you call grid.display() from your template). The following options are supported: show_actions, show_add_link, add_link_title, delete_link_msg. I hope their meaning is self-explaining. Most up-to-date list of these options can be found at the top of datagrid.kid file at turbogears.fastdata.templates sub-package.

Customizing DataGrid representation

Unlike, FastDataGrid?, DataGrid's template provides no customization hook. On the other hand it is really simple and easy to understand so if you need to customize DataGrid's representation simply copy it and tweak as you see fit. To use your template with a DataGrid use template constructor parameter, just like with any Widget.

Advanced DataGrid usage

In addition to simple DataGrid definition shown above, you can use fields parameter in a more "advanced" ways.

Using callable instead of attribute name

Sometimes simply displaying an attribute like 'displayName' is not enough. In case of TG_User object, you may want to format nicely permissions a user posseses. To do this, you provide a callable object instead of the attribute name. The callable is then called with a row object and returned value is inserted in a table cell.

Example:

def format_user_permissions(u):
    # ok, nothing fancy here but you get the idea
    # Hint: use Kid's XML function to return HTML markup.
    return ', '.join(u.permissions) 

users_admin_form = DataGrid(fields=[
    ('ID', 'userId'),
    ('Name', 'displayName'),
    ('Permissions', format_user_permissions),
])

Note that a parameterless method of your model class is a suitable callable object:

class User:
    # ...
    def get_last_login(self):
        "Returns last login's timestamp."

users_admin_form = DataGrid(fields=[
    ('ID', 'userId'),
    ('Name', 'displayName'),
    ('Last logged in', User.get_last_login),
])

Using DataGrid.Column

Instead of two-element tuple you can use an instance of DataGrid.Column (or subclass thereof) when defining DataGrid's fields. This is only useful if you're using custom DataGrid template as well.

The idea of the DataGrid.Column is to be able to specify arbitrary additional options along with a column which are then used in template to alter template's logic.

Example:

users_admin_form = DataGrid(fields=[
    DataGrid.Column('id', 'userId', 'ID',
        options=dict(sortable=True)),
    DataGrid.Column('name', 'displayName', 'Name', 
        options=dict(sortable=True)),
    DataGrid.Column('loggedin', 'get_last_login', 'Last logged in'),
])

Kid template:

  <div py:for="col in columns" py:strip="True">
    <?python sortable = col.get_option('sortable', False) ?>
    ...
    <a py:if="sortable" href="${geturl(sortby=col.name)}">${col.title}</a>
    ...
  </div>

Using DataGrid without a 'model'

All the examples above assumed some model class, like User, being used to display in DataGrid. In fact, you can use DataGrid with any kind of data, not necessarily proper instances. The only "hindrance" is that you have to always specify a callable object as a field's accessor. Example:

grid = DataGrid(fields=[
    ('Name', lambda row: row[1]),
    ('Country', lambda row: row[2]),
    ('Age', lambda row: row[0]),
])

data = [(33, "Anton Bykov", "Bulgaria"), 
    (23, "Joe Doe", "Great Britain"), (44, "Pablo Martelli", "Brazil")]
grid.display(data)

Error handling

DataGrid does not intercept any exception that may raise during iteration or accessing particular object's attribute.