Search code examples
pythonturbogears2

How to place multiple @paginate turbogears 2?


Give a controller and a method like:

import genshi
from pylons import  tmpl_context, request, url
import re
from tg import expose, flash
from tg.controllers import  redirect
from tg.decorators import validate
import tw.forms as twf
from tw.forms.datagrid import DataGrid
from tg.decorators import paginate
from tg.i18n import ugettext as _
from repoze.what.predicates import has_permission
from interlock.lib.base import BaseController
assigned_seller_product_grid = DataGrid(fields=[
    ('Product Name', 'name'),
    ('Producer', 'producer'),
])

unassigned_seller_product_grid = DataGrid(fields=[
    ('Product Name', 'name'),
    ('Producer', 'producer'),
])


class SellerController(BaseController):
    allow_only = has_permission('manage')

    @paginate("unassigned", items_per_page=5, use_prefix=True)
    @paginate("assigned",   items_per_page=5, use_prefix=True)
    @expose('demo.templates.sellers.products')
    def products(self, uid = None, **kw):
        seller = uid is not None and DBSession.query(SellerModel).filter_by(uid=uid, deleted=False).first()
        if uid is not None and not seller:
            flash(_('No Seller ID Defined'), 'warning')
            redirect('/seller/')
        unassigned = DBSession.query(ProductModel).filter(~ProductModel.sellers.any(SellerModel.uid == seller.uid)).filter(ProductModel.deleted == False)
        assigned = DBSession.query(ProductModel).filter(ProductModel.sellers.any(SellerModel.uid == seller.uid)).filter(ProductModel.deleted == False)

        return dict(seller=seller,
                    assigned=assigned,
                    unassigned = unassigned,
                    assigned_grid=assigned_seller_product_grid,
                    unassigned_grid=unassigned_seller_product_grid,
                    page='seller')

And a template like:

<h1>Assigned Products</h1>
<div py:if="assigned">${assigned_grid(assigned)}</div>
<div>${tmpl_context.paginators.assigned.pager(page_param='assigned_page')}</div>
<h1>Unassigned Products</h1>
    <div py:if="unassigned">${unassigned_grid(unassigned)}</div>
    <div>${tmpl_context.paginators.unassigned.pager(page_param='unassigned_page')}</div>

I see TG2 generate correctly the pagination links, but the pagination simply change page just using the first decorator, ignoring the second one. Does anyone faced this problem before ?

Thanks.


Solution

  • The current @paginate decorator on TG doesn't permit to handle multiple paginators on the same page. A new version of the decorator will be available from the next release. In the mean time you can replace the @paginate decorator with the following implementation (just save it as myproject.lib.paginate and import it instead of tg.decorators.paginate):

    from tg.decorators import Decoration
    from tg.paginate import Page
    from tg.util import Bunch, partial
    from tg import request
    
    class paginate(object):
        def __init__(self, name, use_prefix=False,
            items_per_page=10, max_items_per_page=0):
            self.name = name
            prefix = use_prefix and name + '_' or ''
            self.page_param = prefix + 'page'
            self.items_per_page_param = prefix + 'items_per_page'
            self.items_per_page = items_per_page
            self.max_items_per_page = max_items_per_page
    
        def __call__(self, func):
            decoration = Decoration.get_decoration(func)
            decoration.register_hook('before_validate', self.before_validate)
            decoration.register_hook('before_render', self.before_render)
            return func
    
        def before_validate(self, remainder, params):
            page_param = params.pop(self.page_param, None)
            if page_param:
                try:
                    page = int(page_param)
                    if page < 1:
                        raise ValueError
                except ValueError:
                    page = 1
            else:
                page = 1
    
            try:
                paginators_data = request.paginators
            except:
                paginators_data = request.paginators = {'_tg_paginators_params':{}}
    
            paginators_data['_tg_paginators_params'][self.page_param] = page_param
            paginators_data[self.name] = paginator = Bunch()
    
            paginator.paginate_page = page or 1
            items_per_page = params.pop(self.items_per_page_param, None)
            if items_per_page:
                try:
                    items_per_page = min(
                        int(items_per_page), self.max_items_per_page)
                    if items_per_page < 1:
                        raise ValueError
                except ValueError:
                    items_per_page = self.items_per_page
            else:
                items_per_page = self.items_per_page
            paginator.paginate_items_per_page = items_per_page
            paginator.paginate_params = params.copy()
            paginator.paginate_params.update(paginators_data['_tg_paginators_params'])
            if items_per_page != self.items_per_page:
                paginator.paginate_params[self.items_per_page_param] = items_per_page
    
        def before_render(self, remainder, params, output):
            if not isinstance(output, dict) or not self.name in output:
                return
    
            paginator = request.paginators[self.name]
            collection = output[self.name]
            page = Page(collection, paginator.paginate_page,
                paginator.paginate_items_per_page, controller='/')
            page.kwargs = paginator.paginate_params
            if self.page_param != 'name':
                page.pager = partial(page.pager, page_param=self.page_param)
            if not getattr(tmpl_context, 'paginators', None):
                tmpl_context.paginators = Bunch()
            tmpl_context.paginators[self.name] = output[self.name] = page