From: Zygmunt Krynicki Date: Tue, 14 Jun 2011 09:21:36 +0000 (+0200) Subject: Merge branch 'master' of git://github.com/callowayproject/django-pagination X-Git-Tag: release-2.0~9 X-Git-Url: https://git.mdrn.pl/django-pagination.git/commitdiff_plain/bfae3c1f7faa2fb712f34de01f865eb45081ca62?ds=sidebyside;hp=-c Merge branch 'master' of git://github.com/callowayproject/django-pagination Conflicts: pagination/templatetags/pagination_tags.py setup.py --- bfae3c1f7faa2fb712f34de01f865eb45081ca62 diff --combined linaro_django_pagination/templates/pagination/pagination.html index fd925f0,0000000..1bd9ab5 mode 100644,000000..100644 --- a/linaro_django_pagination/templates/pagination/pagination.html +++ b/linaro_django_pagination/templates/pagination/pagination.html @@@ -1,1 -1,0 +1,38 @@@ - {% include pagination_template %} ++{% if is_paginated %} ++{% load i18n %} ++ ++{% endif %} diff --combined linaro_django_pagination/templatetags/pagination_tags.py index 1da25f4,0000000..a78c790 mode 100644,000000..100644 --- a/linaro_django_pagination/templatetags/pagination_tags.py +++ b/linaro_django_pagination/templatetags/pagination_tags.py @@@ -1,296 -1,0 +1,331 @@@ +# Copyright (c) 2008, Eric Florenzano +# Copyright (c) 2010, 2011 Linaro Limited +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + - from django import template +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.core.paginator import Paginator, InvalidPage +from django.http import Http404 - from django.template import TOKEN_BLOCK ++from django.template import ( ++ Context, ++ Library, ++ Node, ++ TOKEN_BLOCK, ++ TemplateSyntaxError, ++ Variable, ++) ++from django.template.loader import select_template + +# TODO, import this normally later on +from linaro_django_pagination.settings import * + + +def do_autopaginate(parser, token): + """ + Splits the arguments to the autopaginate tag and formats them correctly. + + Syntax is: + + autopaginate QUERYSET [PAGINATE_BY] [ORPHANS] [as NAME] + """ + # Check whether there are any other autopaginations are later in this template + expr = lambda obj: (obj.token_type == TOKEN_BLOCK and \ + len(obj.split_contents()) > 0 and obj.split_contents()[0] == "autopaginate") + multiple_paginations = len(filter(expr, parser.tokens)) > 0 + + i = iter(token.split_contents()) + paginate_by = None + queryset_var = None + context_var = None + orphans = None + word = None + try: + word = i.next() + assert word == "autopaginate" + queryset_var = i.next() + word = i.next() + if word != "as": + paginate_by = word + try: + paginate_by = int(paginate_by) + except ValueError: + pass + word = i.next() + if word != "as": + orphans = word + try: + orphans = int(orphans) + except ValueError: + pass + word = i.next() + assert word == "as" + context_var = i.next() + except StopIteration: + pass + if queryset_var is None: - raise template.TemplateSyntaxError( ++ raise TemplateSyntaxError( + "Invalid syntax. Proper usage of this tag is: " + "{%% autopaginate QUERYSET [PAGINATE_BY] [ORPHANS]" + " [as CONTEXT_VAR_NAME] %%}" + ) + return AutoPaginateNode(queryset_var, multiple_paginations, paginate_by, orphans, context_var) + + - class AutoPaginateNode(template.Node): ++class AutoPaginateNode(Node): + """ + Emits the required objects to allow for Digg-style pagination. + + First, it looks in the current context for the variable specified, and using + that object, it emits a simple ``Paginator`` and the current page object + into the context names ``paginator`` and ``page_obj``, respectively. + + It will then replace the variable specified with only the objects for the + current page. + + .. note:: + + It is recommended to use *{% paginate %}* after using the autopaginate + tag. If you choose not to use *{% paginate %}*, make sure to display the + list of available pages, or else the application may seem to be buggy. + """ + def __init__(self, queryset_var, multiple_paginations, paginate_by=None, + orphans=None, context_var=None): + if paginate_by is None: + paginate_by = DEFAULT_PAGINATION + if orphans is None: + orphans = DEFAULT_ORPHANS - self.queryset_var = template.Variable(queryset_var) ++ self.queryset_var = Variable(queryset_var) + if isinstance(paginate_by, int): + self.paginate_by = paginate_by + else: - self.paginate_by = template.Variable(paginate_by) ++ self.paginate_by = Variable(paginate_by) + if isinstance(orphans, int): + self.orphans = orphans + else: - self.orphans = template.Variable(orphans) ++ self.orphans = Variable(orphans) + self.context_var = context_var + self.multiple_paginations = multiple_paginations + + def render(self, context): + if self.multiple_paginations or "paginator" in context: + page_suffix = '_%s' % self.queryset_var + else: + page_suffix = '' + + key = self.queryset_var.var + value = self.queryset_var.resolve(context) + if isinstance(self.paginate_by, int): + paginate_by = self.paginate_by + else: + paginate_by = self.paginate_by.resolve(context) + if isinstance(self.orphans, int): + orphans = self.orphans + else: + orphans = self.orphans.resolve(context) + paginator = Paginator(value, paginate_by, orphans) + try: + request = context['request'] + except KeyError: + raise ImproperlyConfigured( + "You need to enable 'django.core.context_processors.request'." + " See linaro-django-pagination/README file for TEMPLATE_CONTEXT_PROCESSORS details") + try: + page_obj = paginator.page(request.page(page_suffix)) + except InvalidPage: + if INVALID_PAGE_RAISES_404: + raise Http404('Invalid page requested. If DEBUG were set to ' + + 'False, an HTTP 404 page would have been shown instead.') + context[key] = [] + context['invalid_page'] = True + return u'' + if self.context_var is not None: + context[self.context_var] = page_obj.object_list + else: + context[key] = page_obj.object_list + context['paginator'] = paginator + context['page_obj'] = page_obj + context['page_suffix'] = page_suffix + return u'' + + ++class PaginateNode(Node): ++ ++ def __init__(self, template=None): ++ self.template = template ++ ++ def render(self, context): ++ template_list = ['pagination/pagination.html'] ++ to_return = paginate(context) ++ if self.template: ++ template_list.insert(0, self.template) ++ t = select_template(template_list) ++ if not t: ++ return None ++ context = Context(to_return) ++ return t.render(context) ++ ++ ++def do_paginate(parser, token): ++ """ ++ {% paginate [using] [template] %} ++ ++ {% paginate %} ++ {% paginate using paginations/custom_pagination.html %} ++ """ ++ argv = token.contents.split() ++ argc = len(argv) ++ if argc > 3: ++ raise TemplateSyntaxError("Tag %s takes at most 2 argument." % argv[0]) ++ if argc == 1: ++ return PaginateNode() ++ if argc == 3 and argv[1] == 'using': ++ return PaginateNode(template=argv[2]) ++ raise TemplateSyntaxError("Tag %s is invalid. Please check the syntax" % argv[0]) ++ ++ +def paginate(context, window=DEFAULT_WINDOW, margin=DEFAULT_MARGIN): + """ + Renders the ``pagination/pagination.html`` template, resulting in a + Digg-like display of the available pages, given the current page. If there + are too many pages to be displayed before and after the current page, then + elipses will be used to indicate the undisplayed gap between page numbers. + + Requires one argument, ``context``, which should be a dictionary-like data + structure and must contain the following keys: + + ``paginator`` + A ``Paginator`` or ``QuerySetPaginator`` object. + + ``page_obj`` + This should be the result of calling the page method on the + aforementioned ``Paginator`` or ``QuerySetPaginator`` object, given + the current page. + + This same ``context`` dictionary-like data structure may also include: + + ``getvars`` + A dictionary of all of the **GET** parameters in the current request. + This is useful to maintain certain types of state, even when requesting + a different page. - - ``pagination_template`` - A custom template to include in place of the default ``pagination/default.html`` - contents. + + Argument ``window`` is number to pages before/after current page. If window + exceeds pagination border (1 and end), window is moved to left or right. + + Argument ``margin``` is number of pages on start/end of pagination. + Example: + window=2, margin=1, current=6 1 ... 4 5 [6] 7 8 ... 11 + window=2, margin=0, current=1 [1] 2 3 4 5 ... + window=2, margin=0, current=5 ... 3 4 [5] 6 7 ... + window=2, margin=0, current=11 ... 7 8 9 10 [11] + """ + + if window < 0: + raise ValueError('Parameter "window" cannot be less than zero') + if margin < 0: + raise ValueError('Parameter "margin" cannot be less than zero') + try: + paginator = context['paginator'] + page_obj = context['page_obj'] + page_suffix = context.get('page_suffix', '') + page_range = paginator.page_range - pagination_template = context.get('pagination_template', 'pagination/default.html') + # Calculate the record range in the current page for display. + records = {'first': 1 + (page_obj.number - 1) * paginator.per_page} + records['last'] = records['first'] + paginator.per_page - 1 + if records['last'] + paginator.orphans >= paginator.count: + records['last'] = paginator.count + + # figure window + window_start = page_obj.number - window - 1 + window_end = page_obj.number + window + + # solve if window exceeded page range + if window_start < 0: + window_end = window_end - window_start + window_start = 0 + if window_end > paginator.num_pages: + window_start = window_start - (window_end - paginator.num_pages) + window_end = paginator.num_pages + pages = page_range[window_start:window_end] + + # figure margin and add elipses + if margin > 0: + # figure margin + tmp_pages = set(pages) + tmp_pages = tmp_pages.union(page_range[:margin]) + tmp_pages = tmp_pages.union(page_range[-margin:]) + tmp_pages = list(tmp_pages) + tmp_pages.sort() + pages = [] + pages.append(tmp_pages[0]) + for i in range(1, len(tmp_pages)): + # figure gap size => add elipses or fill in gap + gap = tmp_pages[i] - tmp_pages[i - 1] + if gap >= 3: + pages.append(None) + elif gap == 2: + pages.append(tmp_pages[i] - 1) + pages.append(tmp_pages[i]) + else: + if pages[0] != 1: + pages.insert(0, None) + if pages[-1] != paginator.num_pages: + pages.append(None) + + to_return = { + 'MEDIA_URL': settings.MEDIA_URL, + 'STATIC_URL': getattr(settings, "STATIC_URL", None), + 'display_disabled_next_link': DISPLAY_DISABLED_NEXT_LINK, + 'display_disabled_previous_link': DISPLAY_DISABLED_PREVIOUS_LINK, + 'display_page_links': DISPLAY_PAGE_LINKS, + 'is_paginated': paginator.count > paginator.per_page, + 'next_link_decorator': NEXT_LINK_DECORATOR, + 'page_obj': page_obj, + 'page_suffix': page_suffix, + 'pages': pages, - 'pagination_template': pagination_template, + 'paginator': paginator, + 'previous_link_decorator': PREVIOUS_LINK_DECORATOR, + 'records': records, + } + if 'request' in context: + getvars = context['request'].GET.copy() + if 'page%s' % page_suffix in getvars: + del getvars['page%s' % page_suffix] + if len(getvars.keys()) > 0: + to_return['getvars'] = "&%s" % getvars.urlencode() + else: + to_return['getvars'] = '' + return to_return + except (KeyError, AttributeError): + return {} + + - register = template.Library() - register.inclusion_tag( - 'pagination/pagination.html', takes_context=True)(paginate) ++register = Library() ++register.tag('paginate', do_paginate) +register.tag('autopaginate', do_autopaginate) diff --combined setup.py index c653024,7a4de48..2c23e80 mode 100755,100644..100755 --- a/setup.py +++ b/setup.py @@@ -1,68 -1,123 +1,67 @@@ -from setuptools import setup, find_packages - -version = '1.1.0' - -LONG_DESCRIPTION = """ -How to use django-pagination ----------------------------- - -``django-pagination`` allows for easy Digg-style pagination without modifying -your views. - -There are really 5 steps to setting it up with your projects (not including -installation, which is covered in INSTALL.txt in this same directory.) - -1. List this application in the ``INSTALLED_APPS`` portion of your settings - file. Your settings file might look something like:: - - INSTALLED_APPS = ( - # ... - 'pagination', - ) - - -2. Install the pagination middleware. Your settings file might look something - like:: - - MIDDLEWARE_CLASSES = ( - # ... - 'pagination.middleware.PaginationMiddleware', - ) - -3. If it's not already added in your setup, add the request context processor. - Note that context processors are set by default implicitly, so to set them - explicitly, you need to copy and paste this code into your under - the value TEMPLATE_CONTEXT_PROCESSORS:: - - ("django.core.context_processors.auth", - "django.core.context_processors.debug", - "django.core.context_processors.i18n", - "django.core.context_processors.media", - "django.core.context_processors.request") - -4. Add this line at the top of your template to load the pagination tags: - - {% load pagination_tags %} - - -5. Decide on a variable that you would like to paginate, and use the - autopaginate tag on that variable before iterating over it. This could - take one of two forms (using the canonical ``object_list`` as an example - variable): - - {% autopaginate object_list %} - - This assumes that you would like to have the default 20 results per page. - If you would like to specify your own amount of results per page, you can - specify that like so: - - {% autopaginate object_list 10 %} - - Note that this replaces ``object_list`` with the list for the current page, so - you can iterate over the ``object_list`` like you normally would. - - -6. Now you want to display the current page and the available pages, so - somewhere after having used autopaginate, use the paginate inclusion tag: - - {% paginate %} - - This does not take any arguments, but does assume that you have already - called autopaginate, so make sure to do so first. - +#!/usr/bin/env python +# Copyright (c) 2008, Eric Florenzano +# Copyright (c) 2010, 2011 Linaro Limited +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of the author nor the names of other +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -That's it! You have now paginated ``object_list`` and given users of the site -a way to navigate between the different pages--all without touching your views. - - -Optional Settings ------------------- - -In django-pagination, there are no required settings. There are, however, a -small set of optional settings useful for changing the default behavior of the -pagination tags. Here's an overview: -- -``PAGINATION_DEFAULT_PAGINATION`` - The default amount of items to show on a page if no number is specified. +from setuptools import setup, find_packages -``PAGINATION_DEFAULT_WINDOW`` - The number of items to the left and to the right of the current page to - display (accounting for ellipses). -``PAGINATION_DEFAULT_ORPHANS`` - The number of orphans allowed. According to the Django documentation, - orphans are defined as:: - - The minimum number of items allowed on the last page, defaults to zero. +version = "1.1" -``PAGINATION_INVALID_PAGE_RAISES_404`` - Determines whether an invalid page raises an ``Http404`` or just sets the - ``invalid_page`` context variable. ``True`` does the former and ``False`` - does the latter. -""" setup( - name='django-pagination', + name='linaro-django-pagination', version=version, - description="django-pagination", - long_description=LONG_DESCRIPTION, - classifiers=[ - "Programming Language :: Python", - "Topic :: Software Development :: Libraries :: Python Modules", - "Framework :: Django", - "Environment :: Web Environment", - ], + author='Zygmunt Krynicki', + author_email='zygmunt.krynicki@linaro.org', + description="linaro-django-pagination", + long_description=open("README").read(), keywords='pagination,django', - author='Eric Florenzano', - author_email='floguy@gmail.com', - url='http://django-pagination.googlecode.com/', + url='https://github.com/zyga/django-pagination', + test_suite='linaro_django_pagination.test_project.tests.run_tests', license='BSD', packages=find_packages(), + classifiers=[ + "Development Status :: 4 - Beta", + "Environment :: Web Environment", + "Framework :: Django", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + ], + install_requires=[ + 'django >= 1.2', + ], + tests_require=[ + 'django-testproject >= 0.1', + ], include_package_data=True, - zip_safe=False, )