Source

django-simplepagination / simplepagination / __init__.py

Full commit
# coding: utf-8

from functools import partial, update_wrapper

from django.utils.datastructures import MultiValueDictKeyError
from django.core.paginator import Paginator, EmptyPage
from django.http import Http404

from . import settings
from .utils import unicode_urlencode, get_instance_from_path


class SimplePaginator(object):
    """
    Class based decorator.

    SimplePagination decorator must be used along with 'render_to' 
    decorator from django-annoying application
    http://bitbucket.org/offline/django-annoying/wiki/Home
    """

    def __init__(self, key='object_list', style=None, per_page=10, 
                frame_size=8, allow_user_per_page=False, fixed_user_per_page=None, 
                template=None, anchor=None):
        """
        Decorator parameters 

        key - Name of the variable with objects that we paginate.
        style - name of pagination backend.
        per_page - number of objects to show on page.
        frame_size - max pages numbers to show.
        """

        self.key = key
        self.style = style or getattr(settings, "PAGINATION_STYLE", "digg")
        self.backend = get_instance_from_path(settings.PAGINATION_BACKENDS[self.style])
        self.per_page = per_page
        self.frame_size = frame_size
        self.allow_user_per_page = allow_user_per_page
        self.template = template or 'paginator_%s.html' % style
        self.fixed_user_per_page = fixed_user_per_page
        self.anchor = anchor
        
    def __call__(self, function):
        """
        Receive decorated function and return
        function decorated with decorate method
        """
        decorated = partial(self.decorate, function)
        return update_wrapper(decorated, self.decorate)
    
    def decorate(self, function, request, *args, **kwargs):

        # execute view 
        output = function(request, *args, **kwargs)
        
        # only try to paginate if view returned dictionary,
        # in all other cases just return view output.
        if not isinstance(output, dict):
            return output
        
        params = request.GET.copy()
        
        try:
            page_num = int(params.pop('page')[0])
        except (ValueError, KeyError):
            page_num = 1
        
        per_page = self.per_page
        if self.allow_user_per_page and 'per_page' in params:
            try:
                user_per_page = int(params['per_page'])
                if not self.fixed_user_per_page or user_per_page not in self.fixed_user_per_page:
                    per_page = user_per_page
            except (ValueError, KeyError):
                params['per_page'] = self.per_page
        elif 'per_page' in params:
            params.pop('per_page')
        
        try:
            paginate_qs = output.pop(self.key)
        except KeyError:
            raise KeyError("Key '%s' not found in view's returned dictionary" % self.key)
        paginator = Paginator(paginate_qs, per_page)
        
        try:
            page = paginator.page(page_num)
        except EmptyPage:
            raise Http404()
        
        data = {}
        data['page_num'] = page_num # active page number
        data['per_page'] = per_page # items per page
        data['params'] = unicode_urlencode(params) # get parameters
        data['anchor'] = self.anchor # ancor
        data['pages'] = pages = paginator.num_pages # number of pages
        data['paginator_template'] = self.template
        data['allow_user_per_page'] = self.allow_user_per_page

        output[self.key] = page.object_list
        data.update(self.backend.paginate(self.frame_size, pages, page_num))
        output['paginator'] = data
        
        return output

    
paginate = SimplePaginator