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