Tony Landis home

Pagination Class for Couchdbkit Views

When using and have found the package to be very useful.

I have been using CouchDB and and do miss the ability to use the WebHelpers pagination that I grew accustomed to using with SQLAlchemy queries, so I hacked this little class.

This library should work in any web application framework, not just Pylons. Just pass in your params when you call Pager(), in pylons it would be Pager(view, params=request.params)

The concept is simple - just pass a Couchdbkit view into this class and you have an object that contains the origonal couchdbkit view plus a few helper functions for generating a textual status of what page you are currently on, as well as the HTML needed to page through the results.

"""
HTML Pagination Generation Library for Couchdbkit

Example Usage:

       >>> # Get a couchdbkit ViewResults object
       >>> view = Greeting.view('greeting/all')

       >>> # Pass the view into the pager, giving it access to the
       >>> # the params dictionary so the pagination links are 
       >>> # populated with any args from the request: &key=val 
       >>> pager = Pager(view, params={key=val, foo=bar}, limit=20)

       >>> # Show the results status
       >>> pager.status()
       >>> "Page 1 of 10, 200 results found"

       >>> # Show the paging links, will display the first, last, and 10
       >>> # closest pages surrounding the page requested
       >>> pager.pager(10)

       >>> # Access the couchdbkit view:
       >>> pager.view.all()

"""

import math

class Pager(object):
        view = None
        params = None
        page_limit_arg = None
        page_no_arg = None
        result_count = 0
        page_count = 0
        page_limit = 0
        page_no = 1
        def __init__(self, view, params=None, 
                page_limit_arg='page_limit', page_no_arg='page_no', 
                limit=20, **kwargs):
                self.view = view
                self.params = params
                self.page_limit_arg = page_limit_arg
                self.page_no_arg = page_no_arg
                # get the skip / limit values
                self.page_limit = int(params.get(page_limit_arg, limit))
                self.page_no = int(params.get(page_no_arg, 1))
                # set the skip / limit for couchdb
                self.view.params.update(
                        dict(
                                limit=self.page_limit, 
                                skip=(self.page_no-1) * self.page_limit))
                # count results
                self.result_count = view.total_rows
                #self.result_count -= view.offset
                if self.result_count:
                        if self.result_count <= self.page_limit:
                                self.page_count = 1
                        else:
                                self.page_count = \
                                int(math.ceil(float(self.result_count) / float(self.page_limit))) 
        def all(self):
                return self.view.all()
        def status(self, change=False):
                "show the paging status"
                return 'Page %s of %s, %s results found' % (
                        (self.page_no), self.page_count, self.result_count)
        def pager(self, count):
                "show the pager links"
                if not self.page_count > 1: return []
                pages = [1]
                i = self.page_no
                cn = int(math.ceil(count / 2))
                for i in range(cn):
                        ii = self.page_no - cn + i
                        if ii > 1: pages.append(ii)
                for i in range(cn+1):
                        ii = self.page_no + i
                        if 1 < ii > 0: pages.append(ii)
                if self.page_count-1 not in pages:
                        pages.append(self.page_count)
                href = '?page_no=_PG_'
                for k,v in self.params.iteritems():
                        if not k == 'page_no':
                                href += '&%s=%s' % (k,v)
                ln = ''
                last = None
                for i in pages:
                        if last and (last+1) < i:
                                ln += ' ... '
                        if i != self.page_no:
                                ln += '<a class="pager_link" href="%s">%s</a>' \
                                        % (href.replace('_PG_', str(i)), i)
                        else:
                                ln += '<span class="pager_curpage">%i</span>' % i
                        last = i
                return ln

Happy Pagination!