Ticket #2302: paginate.patch
| File paginate.patch, 8.6 KB (added by chrisz, 3 years ago) |
|---|
-
tg/decorators.py
22 22 from webhelpers.paginate import Page 23 23 from pylons import config, request, response 24 24 from pylons.controllers.util import abort 25 from pylons import tmpl_context as c25 from tg import tmpl_context 26 26 from tg.util import partial 27 27 from repoze.what.plugins.pylonshq import ActionProtector, ControllerProtector 28 28 … … 30 30 from tg.flash import flash 31 31 #from tg.controllers import redirect 32 32 33 33 34 class Decoration(object): 34 35 """ Simple class to support 'simple registration' type decorators 35 36 """ … … 304 305 self.content_type, self.engine, self.template, self.exclude_names) 305 306 return func 306 307 308 307 309 def use_custom_format(controller, custom_format): 308 310 """Use use_custom_format in a controller in order to change 309 311 the active @expose decorator when available.""" … … 315 317 316 318 deco.render_custom_format = custom_format 317 319 320 318 321 def override_template(controller, template): 319 322 """Use overide_template in a controller in order to change the 320 323 template that will be used to render the response dictionary … … 384 387 return func 385 388 386 389 387 def paginate(name, items_per_page=10, use_prefix=False): 388 """ 389 Paginate a given collection. 390 class paginate(object): 391 """Paginate a given collection. 390 392 391 393 This decorator is mainly exposing the functionality 392 394 of :func:`webhelpers.paginate`. … … 405 407 406 408 To render the actual pager, use:: 407 409 408 ${ c.paginators.<name>.pager()}410 ${tmpl_context.paginators.<name>.pager()} 409 411 410 where c is the tmpl_context.411 412 412 It is possible to have several :func:`paginate`-decorators for 413 413 one controller action to paginate several collections independently 414 414 from each other. If this is desired, don't forget to set the :attr:`use_prefix`-parameter … … 417 417 :Parameters: 418 418 name 419 419 the collection to be paginated. 420 items_per_page421 the number of items to be rendered. Defaults to 10422 420 use_prefix 423 421 if True, the parameters the paginate 424 422 decorator renders and reacts to are prefixed with 425 423 "<name>_". This allows for multi-pagination. 424 items_per_page 425 the number of items to be rendered. Defaults to 10. 426 max_items_per_page 427 the maximum number of items allowed to be set via parameter. 428 Defaults to 0 (does not allow to change that value). 426 429 427 430 """ 428 prefix = ""429 if use_prefix:430 prefix = name + "_"431 own_parameters = dict(432 page="%spage" % prefix,433 items_per_page="%sitems_per_page" % prefix434 )435 #@decorator436 def _d(f):437 def _w(*args, **kwargs):438 page = int(kwargs.pop(own_parameters["page"], 1))439 real_items_per_page = int(440 kwargs.pop(441 own_parameters['items_per_page'],442 items_per_page))443 431 444 res = f(*args, **kwargs) 445 if isinstance(res, dict) and name in res: 446 additional_parameters = MultiDict() 447 for key, value in request.str_params.iteritems(): 448 if key not in own_parameters: 449 additional_parameters.add(key, value) 432 def __init__(self, name, use_prefix=False, 433 items_per_page=10, max_items_per_page=0): 434 self.name = name 435 prefix = use_prefix and name + '_' or '' 436 self.page_param = prefix + 'page' 437 self.items_per_page_param = prefix + 'items_per_page' 438 self.items_per_page = items_per_page 439 self.max_items_per_page = max_items_per_page 450 440 451 collection = res[name] 452 page = Page( 453 collection, 454 page, 455 items_per_page=real_items_per_page, 456 **additional_parameters.dict_of_lists() 457 ) 458 # wrap the pager so that it will render 459 # the proper page-parameter 460 page.pager = partial(page.pager, 461 page_param=own_parameters["page"]) 462 res[name] = page 463 # this is a bit strange - it appears 464 # as if c returns an empty 465 # string for everything it dosen't know. 466 # I didn't find that documented, so I 467 # just put this in here and hope it works. 468 if not hasattr(c, 'paginators') or type(c.paginators) == str: 469 c.paginators = Bunch() 470 c.paginators[name] = page 471 return res 472 return _w 473 return _d 441 def __call__(self, func): 442 decoration = Decoration.get_decoration(func) 443 decoration.register_hook('before_validate', self.before_validate) 444 decoration.register_hook('before_render', self.before_render) 445 return func 474 446 447 def before_validate(self, remainder, params): 448 page = params.pop(self.page_param, None) 449 if page: 450 try: 451 page = int(page) 452 if page < 1: 453 raise ValueError 454 except ValueError: 455 page = 1 456 else: 457 page = 1 458 request.paginate_page = page or 1 459 items_per_page = params.pop(self.items_per_page_param, None) 460 if items_per_page: 461 try: 462 items_per_page = min( 463 int(items_per_page), self.max_items_per_page) 464 if items_per_page < 1: 465 raise ValueError 466 except ValueError: 467 items_per_page = self.items_per_page 468 else: 469 items_per_page = self.items_per_page 470 request.paginate_items_per_page = items_per_page 471 request.paginate_params = params.copy() 472 if items_per_page != self.items_per_page: 473 request.paginate_params[self.items_per_page_param] = items_per_page 474 475 def before_render(self, remainder, params, output): 476 if not isinstance(output, dict) or not self.name in output: 477 return 478 collection = output[self.name] 479 page = Page(collection, request.paginate_page, 480 request.paginate_items_per_page) 481 page.kwargs = request.paginate_params 482 if self.page_param != 'name': 483 page.pager = partial(page.pager, page_param=self.page_param) 484 if not getattr(tmpl_context, 'paginators', None): 485 tmpl_context.paginators = Bunch() 486 tmpl_context.paginators[self.name] = output[self.name] = page 487 488 475 489 @decorator 476 490 def postpone_commits(func, *args, **kwargs): 477 491 """Turns sqlalchemy commits into flushes in the decorated method … … 488 502 s.commit = old_commit 489 503 return retval 490 504 505 491 506 @decorator 492 507 def without_trailing_slash(func, *args, **kwargs): 493 508 """This decorator allows you to ensure that the URL does not end in "/" … … 512 527 redirect(request.url[:-1]) 513 528 return func(*args, **kwargs) 514 529 530 515 531 @decorator 516 532 def with_trailing_slash(func, *args, **kwargs): 517 533 """This decorator allows you to ensure that the URL ends in "/" … … 543 559 class require(ActionProtector): 544 560 """ 545 561 TurboGears-specific repoze.what-pylons action protector. 546 562 547 563 The default authorization denial handler of this protector will flash 548 564 the message of the unmet predicate with ``warning`` or ``error`` as the 549 565 flash status if the HTTP status code is 401 or 403, respectively. 550 566 551 567 See :class:`allow_only` for controller-wide authorization. 552 568 553 569 """ 554 570 555 571 def default_denial_handler(self, reason): 556 572 """Authorization denial handler for repoze.what-pylons protectors.""" 557 573 if response.status_int == 401: … … 566 582 class allow_only(ControllerProtector): 567 583 """ 568 584 TurboGears-specific repoze.what-pylons controller protector. 569 585 570 586 The default authorization denial handler of this protector will flash 571 587 the message of the unmet predicate with ``warning`` or ``error`` as the 572 588 flash status if the HTTP status code is 401 or 403, respectively, since 573 589 by default the ``__before__`` method of the controller is decorated with 574 590 :class:`require`. 575 591 576 592 If the controller class has the ``_failed_authorization`` *class method*, 577 593 it will replace the default denial handler. 578 594 579 595 """ 580 596 protector = require 581 597 582 598 def __call__(self, cls, *args, **kwargs): 583 599 if hasattr(cls, '_failed_authorization'): 584 600 self.denial_handler = cls._failed_authorization