Package turbogears :: Package widgets :: Module datagrid

Source Code for Module turbogears.widgets.datagrid

  1  """Generic widget to present and manipulate data in a grid (tabular) form. 
  2   
  3  See also: http://trac.turbogears.org/turbogears/wiki/DataGridWidget 
  4   
  5  """ 
  6   
  7  from turbogears.widgets import Widget, CSSLink, static 
  8  from turbogears.widgets.base import CoreWD 
  9   
 10  NoDefault = object() 
 11   
 12  __all__ = ["DataGrid", "PaginateDataGrid"] 
13 14 15 -class DataGrid(Widget):
16 """Generic widget to present and manipulate data in a grid (tabular) form. 17 18 The columns to build the grid from are specified with fields ctor argument 19 which is a list. Currently an element can be either a two-element tuple or 20 instance of DataGrid.Column class. If tuple is used it a Column is then 21 build out of it, first element is assumed to be a title and second element - 22 field accessor. 23 24 You can specify columns' data statically, via fields ctor parameter, or 25 dynamically, by via 'fields' key. 26 27 """ 28 css = [CSSLink(static, "grid.css")] 29 template = "turbogears.widgets.templates.datagrid" 30 fields = None 31
32 - class Column:
33 """Simple struct that describes single DataGrid column. 34 35 Column has: 36 - a name, which allows to uniquely identify column in a DataGrid 37 - getter, which is used to extract field's value 38 - title, which is displayed in the table's header 39 - options, which is a way to carry arbitrary user-defined data 40 - attrwrapper, which is a function returning an object's attribute 41 42 """
43 - def __init__(self, name, 44 getter=None, title=None, options=None, attrwrapper=None):
45 if name is None: 46 raise ValueError('column name is required') 47 if attrwrapper is None: 48 attrwrapper = DataGrid.attrwrapper 49 if getter is None: 50 self.getter = attrwrapper(name) 51 else: 52 if callable(getter): 53 self.getter = getter 54 else: # assume it's an attribute name 55 self.getter = attrwrapper(getter) 56 self.name = name 57 self.title = title is None and name.capitalize() or title 58 self.options = options or {}
59 - def get_option(self, name, default=NoDefault):
60 if name in self.options: 61 return self.options[name] 62 if default is NoDefault: # no such key and no default is given 63 raise KeyError(name) 64 return default
65 - def get_field(self, row):
66 return self.getter(row)
67 - def __str__(self):
68 return "<DataGrid.Column %s>" % self.name
69
70 - class attrwrapper:
71 """Helper class that returns an object's attribute when called. 72 73 This allows to access 'dynamic' attributes (properties) as well as 74 simple static ones, and also allows nested access. 75 76 """
77 - def __init__(self, name):
78 if not isinstance(name, (int, str)): 79 raise ValueError('attribute name must be' 80 ' an integer index or a string attribute') 81 self.name = name
82 - def __call__(self, obj):
83 if isinstance(obj, (dict, list, tuple)): 84 return obj[self.name] 85 for name in self.name.split('.'): 86 obj = getattr(obj, name) 87 if obj is None: 88 break 89 return obj
90
91 - def __init__(self, fields=None, **kw):
92 super(DataGrid, self).__init__(**kw) 93 if fields: 94 self.fields = fields 95 if self.fields is None: 96 self.fields = [] 97 self.columns = self._parse(self.fields)
98
99 - def get_column(self, name):
100 """Return DataGrid.Column with specified name. 101 102 Raises KeyError if no such column exists. 103 104 """ 105 for col in self.columns: 106 if col.name == name: 107 return col 108 raise KeyError(name)
109
110 - def __getitem__(self, name):
111 """Shortcut to get_column.""" 112 return self.get_column(name)
113 114 @staticmethod
115 - def get_field_getter(columns):
116 """Return a function to access the fields of table by row, col.""" 117 idx = {} # index columns by name 118 for col in columns: 119 idx[col.name] = col 120 def _get_field(row, col): 121 return idx[col].get_field(row)
122 return _get_field
123
124 - def update_params(self, d):
125 super(DataGrid, self).update_params(d) 126 if d.get('fields'): 127 fields = d.pop('fields') 128 columns = self._parse(fields) 129 else: 130 columns = self.columns[:] 131 d['columns'] = columns 132 d['get_field'] = self.get_field_getter(columns)
133
134 - def _parse(self, fields):
135 """Parse field specifications into a list of Columns. 136 137 A specification can be a DataGrid.Column, an accessor 138 (attribute name or function), a tuple (title, accessor) 139 or a tuple (title, accessor, options). 140 141 """ 142 columns = [] 143 names = set() # keep track of names to ensure there are no dups 144 for n, col in enumerate(fields): 145 if not isinstance(col, self.Column): 146 if isinstance(col, (str, int)) or callable(col): 147 name_or_f = col 148 title = options = None 149 else: 150 title, name_or_f = col[:2] 151 try: 152 options = col[2] 153 except IndexError: 154 options = None 155 name = 'column-' + str(n) 156 col = self.Column(name, 157 name_or_f, title, options, self.attrwrapper) 158 if col.name in names: 159 raise ValueError('Duplicate column name: %s' % name) 160 columns.append(col) 161 names.add(col.name) 162 return columns
163
164 165 -class DataGridDesc(CoreWD):
166 167 name = "DataGrid" 168 169 for_widget = DataGrid(fields=[('Name', lambda row: row[1]), 170 ('Country', lambda row: row[2]), 171 ('Age', lambda row: row[0])], 172 default=[(33, "Anton Bykov", "Bulgaria"), 173 (23, "Joe Doe", "Great Britain"), 174 (44, "Pablo Martelli", "Brazil")])
175
176 177 -class PaginateDataGrid(DataGrid):
178 """A data grid widget that supports the paginate decorator.""" 179 180 template = "turbogears.widgets.templates.paginate_datagrid"
181
182 183 -class PaginateDataGridDesc(CoreWD):
184 185 name = "PaginateDataGrid" 186 187 for_widget = DataGridDesc.for_widget 188
189 - class Paginate(object):
190 # paginate var mock-up 191 page_count = 5 192 pages = range(1, page_count + 1) 193 limit = 3 194 current_page = page_count // 2 195 href_first = "javascript:alert('This is only a mock-up.')" 196 href_prev = href_next = href_last = href_first 197 get_href = lambda self, page, **kw: self.href_first
198
199 - def display(self, *args, **kw):
200 # activate paginate var provider 201 import turbogears.paginate 202 from cherrypy import request 203 request.paginate = self.Paginate() 204 return super(PaginateDataGridDesc, self).display(*args, **kw)
205