Changeset 3735
- Timestamp:
- 11/19/07 21:17:32 (1 year ago)
- Files:
-
- branches/1.0/CHANGELOG.txt (modified) (3 diffs)
- branches/1.0/turbogears/paginate.py (modified) (6 diffs)
- branches/1.0/turbogears/qstemplates/quickstart/+package+/config/app.cfg_tmpl (modified) (1 diff)
- branches/1.0/turbogears/tests/test_paginate.py (modified) (5 diffs)
- branches/1.0/turbogears/widgets/templates/paginate_datagrid.kid (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
branches/1.0/CHANGELOG.txt
r3720 r3735 8 8 * Introduction of tg.mochikit_suppress to prevent the inclusion of 9 9 the shipped MochiKit 1.3.1. That allows to include custom mochikit versions. 10 * PaginateDataGrid template now makes use of paginate attributes to render 11 the links for first/previous/next/last page. 12 * paginate.href_last returns a special URL that allows paginate decorator 13 to compute the correct last page number at server-side. 10 14 11 15 *Features* … … 18 22 * The tg-admin quickstart command has now an option --svn allowing 19 23 automatic creation of the project in the specified SVN repository. 24 * Introduction of paginate.redirect_on_out_of_range and 25 paginate.redirect_on_last_page, which determine if paginate decorator 26 should raise a redirect when current page is out of bound and the 27 last page is requested, respectively. 20 28 21 29 *Fixes* … … 25 33 * Tickets #1458, #1599 and #1604. Fixed SQLAlchemy and Elixir issues. 26 34 * Ticket #1608. Fixed issues with tg-admin update on a project in SVN. 27 * Ticket #1582 : Fix pagination of SQLAlchemy Query ordering by backrefs28 and synonyms' attributes. It also supports SQLAlchemy 0.4.1, which 35 * Ticket #1582. Fix pagination of SQLAlchemy Query ordering by backrefs 36 and synonyms' attributes. It also supports SQLAlchemy 0.4.1, which 29 37 removed the "properties" accessor on Mapper. 30 38 * Ticket #1605. Ensure paginate links bring the same result when using 31 39 ordering columns with a custom datagrid template. 32 40 * Ticket #1617. Fix pagination of out of bound pages. 41 33 42 34 43 *Contributors* branches/1.0/turbogears/paginate.py
r3718 r3735 26 26 27 27 import turbogears 28 from turbogears.controllers import redirect 28 29 from turbogears.decorator import weak_signature_decorator 29 30 from turbogears.view import variable_providers … … 87 88 return default 88 89 89 page = int(kwpop(1, var_name + '_tgp_no', 'tg_paginate_no')) 90 get = turbogears.config.get 91 92 page = kwpop(1, var_name + '_tgp_no', 'tg_paginate_no') 93 if page == 'last': 94 page = None 95 else: 96 page = int(page) 97 if page < 1: 98 page = 1 99 if get('paginate.redirect_on_out_of_range'): 100 cherrypy.request.params[var_name + '_tgp_no'] = page 101 redirect(cherrypy.request.path, cherrypy.request.params) 102 90 103 limit_ = int( 91 104 kwpop(limit, var_name + '_tgp_limit', 'tg_paginate_limit')) … … 169 182 var_data)) 170 183 184 page_count = int(ceil(float(row_count)/limit_)) 185 186 if page > page_count: 187 page = max(page_count, 1) 188 if get('paginate.redirect_on_out_of_range'): 189 cherrypy.request.params[var_name + '_tgp_no'] = page 190 redirect(cherrypy.request.path, cherrypy.request.params) 191 192 if page is None: 193 page = max(page_count, 1) 194 if get('paginate.redirect_on_last_page'): 195 cherrypy.request.params[var_name + '_tgp_no'] = page 196 redirect(cherrypy.request.path, cherrypy.request.params) 197 171 198 offset = (page-1) * limit_ 172 page_count = int(ceil(float(row_count)/limit_))173 199 174 200 # if it's possible display every page … … 211 237 212 238 global _simulate_offset 213 get = turbogears.config.get214 239 if _simulate_offset is None: 215 240 _simulate_offset = get('paginate.simulate_offset', None) … … 263 288 self.ordering = ordering 264 289 self.row_count = row_count 265 self.first_item = (current_page - 1) * limit + 1290 self.first_item = page_count and ((current_page - 1) * limit + 1) or 0 266 291 self.last_item = min(current_page * limit, row_count) 267 292 self.reversed = False … … 287 312 self.input_values) 288 313 self.input_values.update({ 289 var_name + '_tgp_no': page_count,314 var_name + '_tgp_no': 'last', 290 315 var_name + '_tgp_limit': limit 291 316 }) branches/1.0/turbogears/qstemplates/quickstart/+package+/config/app.cfg_tmpl
r3577 r3735 40 40 # Set to True if the scheduler should be started 41 41 # tg.scheduler = False 42 43 # Set to True to allow paginate decorator redirects when page number gets 44 # out of bound. Useful for getting the real page id in the url 45 # paginate.redirect_on_out_of_range = True 46 47 # Set to True to allow paginate decorator redirects when last page is requested. 48 # This is useful for getting the real last page id in the url 49 # paginate.redirect_on_last_page = True 42 50 43 51 # Set session or cookie branches/1.0/turbogears/tests/test_paginate.py
r3716 r3735 5 5 6 6 from turbogears import config, expose, database 7 from turbogears.controllers import RootController 7 from turbogears.controllers import RootController, url 8 8 from turbogears.database import get_engine, metadata, session, mapper 9 9 from turbogears.paginate import paginate, sort_ordering … … 85 85 " , ".join(["%s=%r" % (k,v) for k,v in paginate.__dict__.items()]) 86 86 87 def assert_ok(body, key, value ):87 def assert_ok(body, key, value, raw=False): 88 88 assert "ok: [paginate" in body 89 expr = "%s=%r " % (key, value) 89 if raw: 90 expr = "%s=%s " % (key, value) 91 else: 92 expr = "%s=%r " % (key, value) 90 93 if expr not in body: 91 94 print body … … 161 164 body = cherrypy.response.body[0] 162 165 assert "fail: paginate does not have 'foobar' attribute" in body 166 167 def test_raw_expectation(self): 168 create_request('/spy_correct_expectation') 169 Spy.assert_ok(cherrypy.response.body[0], 'var_name', 'data') 170 Spy.assert_ok(cherrypy.response.body[0], 'var_name', "'data'", raw=True) 163 171 164 172 … … 250 258 multiple = expose()(multiple) 251 259 260 def empty(self): 261 spy = Spy(var_name='data', pages=[], limit=4, page_count=0, 262 order=None, ordering={}, row_count=0, 263 current_page=1, first_item=0, last_item=0) 264 data = [] 265 return dict(data=data, spy=spy) 266 empty = paginate("data", limit=4)(empty) 267 empty = expose()(empty) 268 252 269 253 270 def setUp(self): … … 375 392 assert '"bar": ["U", "V", "W", "X", "Y", "Z"]' in self.body 376 393 377 # TODO: paginate.input_values 378 379 # TODO: paginate.href_XXX 380 381 # TODO: test 'out of bound' pages 394 def test_out_of_bound_pages(self): 395 for number in range(-3,1): 396 self.request("/basic/?tg_paginate_no=%s" % number) 397 assert '"data": [0, 1, 2, 3]' in self.body 398 Spy.assert_ok(self.body, 'current_page', 1) 399 Spy.assert_ok(self.body, 'first_item', 1) 400 Spy.assert_ok(self.body, 'last_item', 4) 401 402 for number in range(4, 7): 403 self.request("/basic/?tg_paginate_no=%s" % number) 404 print self.body 405 assert '"data": [8, 9]' in self.body 406 Spy.assert_ok(self.body, 'current_page', 3) 407 Spy.assert_ok(self.body, 'first_item', 9) 408 Spy.assert_ok(self.body, 'last_item', 10) 409 410 def test_last_page(self): 411 self.request("/basic/?tg_paginate_no=last") 412 print self.body 413 assert '"data": [8, 9]' in self.body 414 Spy.assert_ok(self.body, 'current_page', 3) 415 Spy.assert_ok(self.body, 'first_item', 9) 416 Spy.assert_ok(self.body, 'last_item', 10) 417 418 def test_href(self): 419 self.request("/basic/?data_tgp_no=0") # out of bound 420 Spy.assert_ok(self.body, 'current_page', 1) 421 Spy.assert_ok(self.body, 'href_first', None) 422 Spy.assert_ok(self.body, 'href_prev', None) 423 Spy.assert_ok(self.body, 'href_next', r"'\/basic\/?data_tgp_no=2&data_tgp_limit=4'", raw=True) 424 Spy.assert_ok(self.body, 'href_last', r"'\/basic\/?data_tgp_no=last&data_tgp_limit=4'", raw=True) 425 426 self.request("/basic/?data_tgp_no=1") 427 Spy.assert_ok(self.body, 'current_page', 1) 428 Spy.assert_ok(self.body, 'href_first', None) 429 Spy.assert_ok(self.body, 'href_prev', None) 430 Spy.assert_ok(self.body, 'href_next', r"'\/basic\/?data_tgp_no=2&data_tgp_limit=4'", raw=True) 431 Spy.assert_ok(self.body, 'href_last', r"'\/basic\/?data_tgp_no=last&data_tgp_limit=4'", raw=True) 432 433 self.request("/basic/?data_tgp_no=2") 434 Spy.assert_ok(self.body, 'current_page', 2) 435 Spy.assert_ok(self.body, 'href_first', r"'\/basic\/?data_tgp_no=1&data_tgp_limit=4'", raw=True) 436 Spy.assert_ok(self.body, 'href_prev', r"'\/basic\/?data_tgp_no=1&data_tgp_limit=4'", raw=True) 437 Spy.assert_ok(self.body, 'href_next', r"'\/basic\/?data_tgp_no=3&data_tgp_limit=4'", raw=True) 438 Spy.assert_ok(self.body, 'href_last', r"'\/basic\/?data_tgp_no=last&data_tgp_limit=4'", raw=True) 439 440 self.request("/basic/?data_tgp_no=3") 441 Spy.assert_ok(self.body, 'current_page', 3) 442 Spy.assert_ok(self.body, 'href_first', r"'\/basic\/?data_tgp_no=1&data_tgp_limit=4'", raw=True) 443 Spy.assert_ok(self.body, 'href_prev', r"'\/basic\/?data_tgp_no=2&data_tgp_limit=4'", raw=True) 444 Spy.assert_ok(self.body, 'href_next', None) 445 Spy.assert_ok(self.body, 'href_last', None) 446 447 self.request("/basic/?data_tgp_no=4") # out of bound ! 448 Spy.assert_ok(self.body, 'current_page', 3) 449 Spy.assert_ok(self.body, 'href_first', r"'\/basic\/?data_tgp_no=1&data_tgp_limit=4'", raw=True) 450 Spy.assert_ok(self.body, 'href_prev', r"'\/basic\/?data_tgp_no=2&data_tgp_limit=4'", raw=True) 451 Spy.assert_ok(self.body, 'href_next', None) 452 Spy.assert_ok(self.body, 'href_last', None) 453 454 # empty data 455 self.request("/empty") 456 Spy.assert_ok(self.body, 'current_page', 1) 457 Spy.assert_ok(self.body, 'href_first', None) 458 Spy.assert_ok(self.body, 'href_prev', None) 459 Spy.assert_ok(self.body, 'href_next', None) 460 Spy.assert_ok(self.body, 'href_last', None) 461 462 def test_empty_data(self): 463 self.request("/empty") 464 print self.body 465 assert '"data": []' in self.body 466 382 467 383 468 branches/1.0/turbogears/widgets/templates/paginate_datagrid.kid
r1743 r3735 1 1 <div xmlns:py="http://purl.org/kid/ns#"> 2 3 <table> 2 <table> 4 3 <tr> 5 4 <td> … … 8 7 <td width="50" align="right"> 9 8 10 <span py:if=" not tg.paginate.current_page == 1">11 <a href="${tg.paginate. get_href(1)}"><<</a>12 <a href="${tg.paginate. get_href(tg.paginate.current_page-1)}"><</a>9 <span py:if="tg.paginate.href_prev"> 10 <a href="${tg.paginate.href_first}"><<</a> 11 <a href="${tg.paginate.href_prev}"><</a> 13 12 </span> 14 13 </td> … … 22 21 </td> 23 22 <td width="50"> 24 <span py:if="tg.paginate. pages and not tg.paginate.current_page == tg.paginate.page_count">25 <a href="${tg.paginate. get_href(tg.paginate.current_page+1)}">></a>26 <a href="${tg.paginate. get_href(tg.paginate.page_count)}">>></a>23 <span py:if="tg.paginate.href_next"> 24 <a href="${tg.paginate.href_next}">></a> 25 <a href="${tg.paginate.href_last}">>></a> 27 26 </span> 28 27 29 28 </td> 30 </tr> 31 </table> 29 </tr> 30 </table> 32 31 </td> 33 32 </tr> … … 37 36 <thead py:if="columns"> 38 37 <th py:for="i, col in enumerate(columns)" class="col_${i}"> 39 <a py:if="col.get_option('sortable', False) and getattr(tg, 'paginate', False)" 38 <a py:if="col.get_option('sortable', False) and getattr(tg, 'paginate', False)" 40 39 href="${tg.paginate.get_href(1, col.name, col.get_option('reverse_order', False))}">${col.title}</a> 41 40 <span py:if="not getattr(tg, 'paginate', False) or not col.get_option('sortable', False)" py:replace="col.title"/>