Changeset 2404
- Timestamp:
- 01/19/07 11:11:44 (2 years ago)
- Files:
-
- branches/1.0/turbogears/paginate.py (modified) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
branches/1.0/turbogears/paginate.py
r2326 r2404 1 import re 2 import types 1 3 from math import ceil 2 4 import logging … … 6 8 from sqlobject.main import SelectResults 7 9 except ImportError: 8 pass 9 10 SelectResults = None 11 12 try: 13 # Can't depend on sqlalchemy being available. 14 import sqlalchemy 15 from sqlalchemy.ext.selectresults import SelectResults as SASelectResults 16 except ImportError: 17 SASelectResults = None 18 10 19 import turbogears 11 20 from turbogears.decorator import weak_signature_decorator 12 21 from turbogears.view import variable_providers 22 from formencode.variabledecode import variable_encode, variable_decode 13 23 14 24 log = logging.getLogger("turbogears.paginate") … … 20 30 page = int(kw.pop('tg_paginate_no', 1)) 21 31 limit_ = int(kw.pop('tg_paginate_limit', limit)) 22 order = kw.pop('tg_paginate_order', default_order) 23 reversed = kw.pop('tg_paginate_reversed', None) 24 32 order = kw.pop('tg_paginate_order', None) 33 ordering = kw.pop('tg_paginate_ordering', {}) 34 35 # Convert ordering str to a dict. 36 if ordering: 37 ordering = convert_ordering(ordering) 38 25 39 if not allow_limit_override: 26 40 limit_ = limit 27 28 log.debug("Pagination params: page=%s, limit=%s, order=%s ,"29 " reversed=%s", page, limit_, order, reversed)41 42 log.debug("Pagination params: page=%s, limit=%s, order=%s " 43 "", page, limit_, order) 30 44 31 45 # get the output from the decorated function … … 41 55 raise "If you want to enable ordering you need " \ 42 56 "to provide a default_order" 43 57 elif default_order and not ordering: 58 ordering = {default_order:[0, True]} 59 elif ordering and order: 60 sort_ordering(ordering, order) 61 log.debug('ordering %s' % ordering) 62 44 63 row_count = 0 45 if isinstance(var_data, SelectResults): 64 if (SelectResults and isinstance(var_data, SelectResults)) or \ 65 (SASelectResults and isinstance(var_data, SASelectResults)): 46 66 row_count = var_data.count() 47 col = getattr(var_data.sourceClass.q, order, None) 48 if default_order: 49 if col: 50 var_data = var_data.orderBy(col) 67 if ordering: 68 # Build order_by list. 69 order_cols = range(len(ordering)) 70 for (colname, order_opts) in ordering.items(): 71 col = sql_get_column(colname, var_data) 72 if not col: 73 raise StandardError, "The order column (%s) doesn't exist" % colname 74 order_by_expr = sql_order_col(col, order_opts[1]) 75 order_cols[order_opts[0]] = order_by_expr 76 # May need to address potential of ordering already 77 # existing in var_data. 78 # SO and SA differ on this method name. 79 if hasattr(var_data, 'orderBy'): 80 var_data = var_data.orderBy(order_cols) 51 81 else: 52 raise "The order column (%s) doesn't exist" % order 53 if reversed: 54 var_data = var_data.reversed() 82 var_data = var_data.order_by(order_cols) 55 83 elif isinstance(var_data, list): 56 84 row_count = len(var_data) … … 71 99 # which one should we use? cherrypy.request.input_values or kw? 72 100 #input_values = cherrypy.request.input_values.copy() 73 input_values = kw.copy() 101 ##input_values = kw.copy() 102 input_values = variable_encode(cherrypy.request.params.copy()) 74 103 input_values.pop('self', None) 75 104 for input_key in input_values.keys(): 105 if input_key.startswith('tg_paginate'): 106 del input_values[input_key] 107 76 108 cherrypy.request.paginate = Paginate(current_page=page, 77 limit=limit_,78 pages=pages_to_show,79 page_count=page_count,80 input_values=input_values,81 order=order,82 reversed=reversed)83 109 limit=limit_, 110 pages=pages_to_show, 111 page_count=page_count, 112 input_values=input_values, 113 order=order, 114 ordering=ordering) 115 84 116 # we replace the var with the sliced one 85 117 endpoint = offset + limit_ … … 102 134 """class for variable provider""" 103 135 def __init__(self, current_page, pages, page_count, input_values, 104 limit, order, reversed): 136 limit, order, ordering): 137 105 138 self.pages = pages 106 139 self.limit = limit … … 109 142 self.input_values = input_values 110 143 self.order = order 111 self.reversed = reversed 112 113 self.input_values.update(dict(tg_paginate_limit=limit, 114 tg_paginate_order=order, 115 tg_paginate_reversed=reversed)) 144 self.ordering = ordering 145 self.reversed = False 146 147 # Should reversed be true? 148 for (field_name, ordering_values) in ordering.items(): 149 if ordering_values[0] == 0 and not ordering_values[1]: 150 self.reversed = True 151 152 # If ordering is empty, don't add it. 153 input_values = dict(tg_paginate_limit=limit) 154 if ordering: 155 input_values['tg_paginate_ordering'] = ordering 156 self.input_values.update(input_values) 157 116 158 if current_page < page_count: 117 159 self.input_values.update(dict( … … 141 183 142 184 def get_href(self, page, order=None, reverse_order=None): 185 # Note that reverse_order is not used. It should be cleaned up here 186 # and in the template. I'm not removing it now because I don't want 187 # to break the API. 188 order = order or None 189 self.input_values['tg_paginate_no'] = page 143 190 if order: 144 if order == self.order: 145 if self.reversed: 146 reversed = None 147 else: 148 reversed = True 149 else: 150 reversed = None 151 if reverse_order: 152 if reversed: 153 reversed = None 154 else: 155 reversed = True 156 else: 157 order = self.order 158 reversed = self.reversed 159 160 self.input_values.update(dict(tg_paginate_no=page, 161 tg_paginate_order=order, 162 tg_paginate_reversed=reversed)) 163 191 self.input_values['tg_paginate_order'] = order 192 164 193 return turbogears.url('', self.input_values) 165 166 194 167 195 def _select_pages_to_show(current_page, page_count, max_pages): … … 190 218 191 219 return range(start, end+1) 220 221 def sort_ordering(ordering, sort_name): 222 """Rearrange ordering based on sort_name.""" 223 log.debug('sort called with %s and %s' % (ordering, sort_name)) 224 if sort_name not in ordering: 225 ordering[sort_name] = [-1, True] 226 if ordering[sort_name][0] == 0: 227 # Flip 228 ordering[sort_name][1] = not ordering[sort_name][1] 229 else: 230 ordering[sort_name][0] = 0 231 for key in ordering.keys(): 232 if key != sort_name and ordering[key][0] < len(ordering) - 1: 233 ordering[key][0] += 1 234 log.debug('sort results is %s and %s' % (ordering, sort_name)) 235 236 def sql_get_column(colname, var_data): 237 """Return a column from var_data based on colname.""" 238 if isinstance(var_data, SelectResults): 239 col = getattr(var_data.sourceClass.q, colname, None) 240 elif isinstance(var_data, SASelectResults): 241 col = getattr(var_data._query.table.c, colname, None) 242 else: 243 raise StandardError, 'expected SelectResults' 244 return col 245 246 def sql_order_col(col, ascending=True): 247 """Return an ordered col for col.""" 248 if isinstance(col, sqlalchemy.schema.Column): 249 if ascending: 250 order_col = sqlalchemy.sql.asc(col) 251 else: 252 order_col = sqlalchemy.sql.desc(col) 253 elif isinstance(col, types.InstanceType): 254 # I don't like using InstanceType, but that's what sqlobject col type 255 # is. 256 if ascending: 257 order_col = col 258 else: 259 order_col = sqlobject.DESC(col) 260 else: 261 raise StandardError, 'expected Column, but got %s' % str(type(col)) 262 return order_col 263 264 # Ordering re: 265 ordering_expr = re.compile(r"('\w+'): ?\[(\d+), ?(True|False)\]") 266 267 def convert_ordering(ordering): 268 """Covert ordering unicode string to dict.""" 269 270 log.debug('ordering received %s' % str(ordering)) 271 272 # eval would be simple, but insecure. 273 if not isinstance(ordering, (str, unicode)): 274 raise ValueError, "ordering should be string or unicode." 275 new_ordering = {} 276 if ordering == u"{}": 277 pass 278 else: 279 try: 280 ordering_info_find = ordering_expr.findall(ordering) 281 emsg = "Didn't match ordering for %s." % str(ordering) 282 assert len(ordering_info_find) > 0, emsg 283 for ordering_info in ordering_info_find: 284 ordering_key = str(ordering_info[0]).strip("'") 285 ordering_order = int(ordering_info[1]) 286 ordering_reverse = bool(ordering_info[2] == 'True') 287 new_ordering[ordering_key] = [ordering_order, 288 ordering_reverse] 289 except StandardError, e: 290 log.debug('FAILED to convert ordering.') 291 new_ordering = {} 292 log.debug('ordering converted to %s' % str(new_ordering)) 293 return new_ordering