Repository: ericflo/django-pagination
Branch: master
Commit: d1a8f0f9fb21
Files: 33
Total size: 39.4 KB
Directory structure:
gitextract_lcedyun1/
├── .gitignore
├── CONTRIBUTORS.txt
├── LICENSE.txt
├── MANIFEST.in
├── README.md
├── docs/
│ ├── index.txt
│ ├── install.txt
│ └── usage.txt
├── pagination/
│ ├── __init__.py
│ ├── locale/
│ │ ├── cs/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── de/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── es/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── fr/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── pl/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── pt/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ └── pt_BR/
│ │ └── LC_MESSAGES/
│ │ ├── django.mo
│ │ └── django.po
│ ├── middleware.py
│ ├── models.py
│ ├── paginator.py
│ ├── templates/
│ │ └── pagination/
│ │ └── pagination.html
│ ├── templatetags/
│ │ ├── __init__.py
│ │ └── pagination_tags.py
│ └── tests.py
├── setup.py
└── tests/
├── runtests.py
└── settings.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.pyc
dist
*.egg-info
================================================
FILE: CONTRIBUTORS.txt
================================================
Eric Florenzano <floguy@gmail.com>
Martin Mahner <martin@mahner.org>
James Tauber <jtauber@gmail.com>
================================================
FILE: LICENSE.txt
================================================
Copyright (c) 2008, Eric Florenzano
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.
================================================
FILE: MANIFEST.in
================================================
include LICENSE.txt
include CONTRIBUTORS.txt
recursive-include docs *
recursive-include pagination/templates/pagination *.html
recursive-include pagination/locale *.mo *.po
================================================
FILE: README.md
================================================
This project is no longer maintained.
A maintained fork of this project is available at: https://github.com/pydanny/dj-pagination
================================================
FILE: docs/index.txt
================================================
#################
django-pagination
#################
Django-pagination is a set of utilities for creating robust pagination tools
throughout a django application.
Contents:
.. toctree::
install.txt
usage.txt
================================================
FILE: docs/install.txt
================================================
Installing the latest development version of django-pagination
---------------------------------------------------------------
To install, first check out the latest version of the application from
subversion:
svn co http://django-pagination.googlecode.com/svn/trunk django-pagination
Now, link the inner ``pagination`` project to your Python path:
sudo ln -s `pwd`/pagination SITE_PACKAGES_DIR/pagination
If you don't know the location of your site packages directory, this hack might
do the trick for you:
sudo ln -s `pwd`/pagination `python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"`/pagination
Now it's installed! Please see usage.txt for information on how to use this
application in your projects.
Installing via setup.py
-----------------------
Included with this application is a file named ``setup.py``. It's possible to
use this file to install this application to your system, by invoking the
following command:
sudo python setup.py install
Once that's done, you should be able to begin using django-pagination at will.
Installing via setuptools
-------------------------
If you have setuptools_ installed, you can simply run the following command
to install django-pagination:
sudo easy_install django-pagination
.. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools
================================================
FILE: docs/usage.txt
================================================
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.
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.
A Note About Uploads
--------------------
It is important, when using django-pagination in conjunction with file uploads,
to be aware of when ``request.page`` is accessed. As soon as ``request.page``
is accessed, ``request.upload_handlers`` is frozen and cannot be altered in any
way. It's a good idea to access the ``page`` attribute on the request object
as late as possible in 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.
``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.
``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.
================================================
FILE: pagination/__init__.py
================================================
================================================
FILE: pagination/locale/cs/LC_MESSAGES/django.po
================================================
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-03-16 16:26+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: templates/pagination/pagination.html:5
#: templates/pagination/pagination.html:7
msgid "previous"
msgstr "předchozí"
#: templates/pagination/pagination.html:21
#: templates/pagination/pagination.html:23
msgid "next"
msgstr "další"
================================================
FILE: pagination/locale/de/LC_MESSAGES/django.po
================================================
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-03-16 16:26+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: templates/pagination/pagination.html:5
#: templates/pagination/pagination.html:7
msgid "previous"
msgstr "Zurück"
#: templates/pagination/pagination.html:21
#: templates/pagination/pagination.html:23
msgid "next"
msgstr "Weiter"
================================================
FILE: pagination/locale/es/LC_MESSAGES/django.po
================================================
# django-pagination Spanish translation
# Copyright (C) 2010, Miguel Araujo
# This file is distributed under the same license as the django-pagination package.
# Miguel Araujo <miguel.araujo.perez@gmail.com>, 2010
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-09-16 16:32+0200\n"
"PO-Revision-Date: 2010-09-16 16:38+0200\n"
"Last-Translator: Miguel Araujo <miguel.araujo.perez@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: templates/pagination/pagination.html:5
#: templates/pagination/pagination.html:7
msgid "previous"
msgstr "anterior"
#: templates/pagination/pagination.html:21
#: templates/pagination/pagination.html:23
msgid "next"
msgstr "siguiente"
================================================
FILE: pagination/locale/fr/LC_MESSAGES/django.po
================================================
# django-pagination French translation.
# Copyright (C) 2008, Julien Demoor
# This file is distributed under the same license as the django-pagination package.
# Julien Demoor <julien@jdemoor.com>, 2008
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2008-10-24 00:41-0700\n"
"PO-Revision-Date: 2008-10-19 10:19+0200\n"
"Last-Translator: Julien Demoor <julien@jdemoor.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: templates/pagination/pagination.html:5
#: templates/pagination/pagination.html:7
msgid "previous"
msgstr "précédente"
#: templates/pagination/pagination.html:21
#: templates/pagination/pagination.html:23
msgid "next"
msgstr "suivante"
================================================
FILE: pagination/locale/pl/LC_MESSAGES/django.po
================================================
# Polish translation of django-pagination.
# Copyright (C) 2008, django-pagination team
# This file is distributed under the same license as the django-pagination package.
# Jarek Zgoda <jarek.zgoda@gmail.com>, 2008.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: 1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2008-10-24 00:41-0700\n"
"PO-Revision-Date: 2008-10-20 20:52+0200\n"
"Last-Translator: Jarek Zgoda <jarek.zgoda@gmail.com>\n"
"Language-Team: PL <pl@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: templates/pagination/pagination.html:5
#: templates/pagination/pagination.html:7
msgid "previous"
msgstr "poprzednia"
#: templates/pagination/pagination.html:21
#: templates/pagination/pagination.html:23
msgid "next"
msgstr "następna"
================================================
FILE: pagination/locale/pt/LC_MESSAGES/django.po
================================================
# django-pagination Portuguese translation.
# Copyright (C) 2008, Alcides Fonseca
# http://alcidesfonseca.com
# This file is distributed under the WTFPL
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2008-10-24 00:41-0700\n"
"PO-Revision-Date: 2008-10-19 10:19+0200\n"
"Last-Translator: Alcides Fonseca <me@alcidesfonseca.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: templates/pagination/pagination.html:5
#: templates/pagination/pagination.html:7
msgid "previous"
msgstr "anterior"
#: templates/pagination/pagination.html:21
#: templates/pagination/pagination.html:23
msgid "next"
msgstr "próximo"
================================================
FILE: pagination/locale/pt_BR/LC_MESSAGES/django.po
================================================
# django-pagination in Portuguese (Brazil).
# Copyright (C) 2011, Tiago Samaha Cordeiro
# This file is distributed under the same license as the django-pagination package.
# Tiago Samaha Cordeiro <tiagosamaha@gmail.com>, 2011.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: 1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-01-16 22:06-0200\n"
"PO-Revision-Date: 2011-01-16 22:15-3\n"
"Last-Translator: Tiago Samaha Cordeiro <tiagosamaha@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: Portuguese Brazil\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: templates/pagination/pagination.html:5
#: templates/pagination/pagination.html:7
msgid "previous"
msgstr "anterior"
#: templates/pagination/pagination.html:21
#: templates/pagination/pagination.html:23
msgid "next"
msgstr "próximo"
================================================
FILE: pagination/middleware.py
================================================
def get_page(self, suffix):
"""
A function which will be monkeypatched onto the request to get the current
integer representing the current page.
"""
try:
return int(self.REQUEST['page%s' % suffix])
except (KeyError, ValueError, TypeError):
return 1
class PaginationMiddleware(object):
"""
Inserts a variable representing the current page onto the request object if
it exists in either **GET** or **POST** portions of the request.
"""
def process_request(self, request):
request.__class__.page = get_page
================================================
FILE: pagination/models.py
================================================
================================================
FILE: pagination/paginator.py
================================================
from django.core.paginator import Paginator, Page, PageNotAnInteger, EmptyPage
class InfinitePaginator(Paginator):
"""
Paginator designed for cases when it's not important to know how many total
pages. This is useful for any object_list that has no count() method or can
be used to improve performance for MySQL by removing counts.
The orphans parameter has been removed for simplicity and there's a link
template string for creating the links to the next and previous pages.
"""
def __init__(self, object_list, per_page, allow_empty_first_page=True,
link_template='/page/%d/'):
orphans = 0 # no orphans
super(InfinitePaginator, self).__init__(object_list, per_page, orphans,
allow_empty_first_page)
# no count or num pages
del self._num_pages, self._count
# bonus links
self.link_template = link_template
def validate_number(self, number):
"""
Validates the given 1-based page number.
"""
try:
number = int(number)
except ValueError:
raise PageNotAnInteger('That page number is not an integer')
if number < 1:
raise EmptyPage('That page number is less than 1')
return number
def page(self, number):
"""
Returns a Page object for the given 1-based page number.
"""
number = self.validate_number(number)
bottom = (number - 1) * self.per_page
top = bottom + self.per_page
page_items = self.object_list[bottom:top]
# check moved from validate_number
if not page_items:
if number == 1 and self.allow_empty_first_page:
pass
else:
raise EmptyPage('That page contains no results')
return InfinitePage(page_items, number, self)
def _get_count(self):
"""
Returns the total number of objects, across all pages.
"""
raise NotImplementedError
count = property(_get_count)
def _get_num_pages(self):
"""
Returns the total number of pages.
"""
raise NotImplementedError
num_pages = property(_get_num_pages)
def _get_page_range(self):
"""
Returns a 1-based range of pages for iterating through within
a template for loop.
"""
raise NotImplementedError
page_range = property(_get_page_range)
class InfinitePage(Page):
def __repr__(self):
return '<Page %s>' % self.number
def has_next(self):
"""
Checks for one more item than last on this page.
"""
try:
next_item = self.paginator.object_list[
self.number * self.paginator.per_page]
except IndexError:
return False
return True
def end_index(self):
"""
Returns the 1-based index of the last object on this page,
relative to total objects found (hits).
"""
return ((self.number - 1) * self.paginator.per_page +
len(self.object_list))
#Bonus methods for creating links
def next_link(self):
if self.has_next():
return self.paginator.link_template % (self.number + 1)
return None
def previous_link(self):
if self.has_previous():
return self.paginator.link_template % (self.number - 1)
return None
class FinitePaginator(InfinitePaginator):
"""
Paginator for cases when the list of items is already finite.
A good example is a list generated from an API call. This is a subclass
of InfinitePaginator because we have no idea how many items exist in the
full collection.
To accurately determine if the next page exists, a FinitePaginator MUST be
created with an object_list_plus that may contain more items than the
per_page count. Typically, you'll have an object_list_plus with one extra
item (if there's a next page). You'll also need to supply the offset from
the full collection in order to get the page start_index.
This is a very silly class but useful if you love the Django pagination
conventions.
"""
def __init__(self, object_list_plus, per_page, offset=None,
allow_empty_first_page=True, link_template='/page/%d/'):
super(FinitePaginator, self).__init__(object_list_plus, per_page,
allow_empty_first_page, link_template)
self.offset = offset
def validate_number(self, number):
super(FinitePaginator, self).validate_number(number)
# check for an empty list to see if the page exists
if not self.object_list:
if number == 1 and self.allow_empty_first_page:
pass
else:
raise EmptyPage('That page contains no results')
return number
def page(self, number):
"""
Returns a Page object for the given 1-based page number.
"""
number = self.validate_number(number)
# remove the extra item(s) when creating the page
page_items = self.object_list[:self.per_page]
return FinitePage(page_items, number, self)
class FinitePage(InfinitePage):
def has_next(self):
"""
Checks for one more item than last on this page.
"""
try:
next_item = self.paginator.object_list[self.paginator.per_page]
except IndexError:
return False
return True
def start_index(self):
"""
Returns the 1-based index of the first object on this page,
relative to total objects in the paginator.
"""
## TODO should this holler if you haven't defined the offset?
return self.paginator.offset
================================================
FILE: pagination/templates/pagination/pagination.html
================================================
{% if is_paginated %}
{% load i18n %}
<div class="pagination">
{% if page_obj.has_previous %}
<a href="?page{{ page_suffix }}={{ page_obj.previous_page_number }}{{ getvars }}{{ hashtag }}" class="prev">‹‹ {% trans "previous" %}</a>
{% else %}
<span class="disabled prev">‹‹ {% trans "previous" %}</span>
{% endif %}
{% for page in pages %}
{% if page %}
{% ifequal page page_obj.number %}
<span class="current page">{{ page }}</span>
{% else %}
<a href="?page{{ page_suffix }}={{ page }}{{ getvars }}{{ hashtag }}" class="page">{{ page }}</a>
{% endifequal %}
{% else %}
...
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a href="?page{{ page_suffix }}={{ page_obj.next_page_number }}{{ getvars }}{{ hashtag }}" class="next">{% trans "next" %} ››</a>
{% else %}
<span class="disabled next">{% trans "next" %} ››</span>
{% endif %}
</div>
{% endif %}
================================================
FILE: pagination/templatetags/__init__.py
================================================
================================================
FILE: pagination/templatetags/pagination_tags.py
================================================
try:
set
except NameError:
from sets import Set as set
from django import template
from django.template import TOKEN_BLOCK
from django.http import Http404
from django.core.paginator import Paginator, InvalidPage
from django.conf import settings
register = template.Library()
DEFAULT_PAGINATION = getattr(settings, 'PAGINATION_DEFAULT_PAGINATION', 20)
DEFAULT_WINDOW = getattr(settings, 'PAGINATION_DEFAULT_WINDOW', 4)
DEFAULT_ORPHANS = getattr(settings, 'PAGINATION_DEFAULT_ORPHANS', 0)
INVALID_PAGE_RAISES_404 = getattr(settings,
'PAGINATION_INVALID_PAGE_RAISES_404', False)
def do_autopaginate(parser, token):
"""
Splits the arguments to the autopaginate tag and formats them correctly.
"""
# 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
split = token.split_contents()
as_index = None
context_var = None
for i, bit in enumerate(split):
if bit == 'as':
as_index = i
break
if as_index is not None:
try:
context_var = split[as_index + 1]
except IndexError:
raise template.TemplateSyntaxError("Context variable assignment " +
"must take the form of {%% %r object.example_set.all ... as " +
"context_var_name %%}" % split[0])
del split[as_index:as_index + 2]
if len(split) == 2:
return AutoPaginateNode(split[1], multiple_paginations=multiple_paginations)
elif len(split) == 3:
return AutoPaginateNode(split[1], paginate_by=split[2],
context_var=context_var, multiple_paginations=multiple_paginations)
elif len(split) == 4:
try:
orphans = int(split[3])
except ValueError:
raise template.TemplateSyntaxError(u'Got %s, but expected integer.'
% split[3])
return AutoPaginateNode(split[1], paginate_by=split[2], orphans=orphans,
context_var=context_var, multiple_paginations=multiple_paginations)
else:
raise template.TemplateSyntaxError('%r tag takes one required ' +
'argument and one optional argument' % split[0])
class AutoPaginateNode(template.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=DEFAULT_PAGINATION,
orphans=DEFAULT_ORPHANS, context_var=None):
self.queryset_var = template.Variable(queryset_var)
if isinstance(paginate_by, int):
self.paginate_by = paginate_by
else:
self.paginate_by = template.Variable(paginate_by)
self.orphans = orphans
self.context_var = context_var
self.multiple_paginations = multiple_paginations
def render(self, context):
if self.multiple_paginations or context.has_key('paginator'):
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)
paginator = Paginator(value, paginate_by, self.orphans)
try:
page_obj = paginator.page(context['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''
def paginate(context, window=DEFAULT_WINDOW, hashtag=''):
"""
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.
"""
try:
paginator = context['paginator']
page_obj = context['page_obj']
page_suffix = context.get('page_suffix', '')
page_range = paginator.page_range
# 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
# First and last are simply the first *n* pages and the last *n* pages,
# where *n* is the current window size.
first = set(page_range[:window])
last = set(page_range[-window:])
# Now we look around our current page, making sure that we don't wrap
# around.
current_start = page_obj.number-1-window
if current_start < 0:
current_start = 0
current_end = page_obj.number-1+window
if current_end < 0:
current_end = 0
current = set(page_range[current_start:current_end])
pages = []
# If there's no overlap between the first set of pages and the current
# set of pages, then there's a possible need for elusion.
if len(first.intersection(current)) == 0:
first_list = list(first)
first_list.sort()
second_list = list(current)
second_list.sort()
pages.extend(first_list)
diff = second_list[0] - first_list[-1]
# If there is a gap of two, between the last page of the first
# set and the first page of the current set, then we're missing a
# page.
if diff == 2:
pages.append(second_list[0] - 1)
# If the difference is just one, then there's nothing to be done,
# as the pages need no elusion and are correct.
elif diff == 1:
pass
# Otherwise, there's a bigger gap which needs to be signaled for
# elusion, by pushing a None value to the page list.
else:
pages.append(None)
pages.extend(second_list)
else:
unioned = list(first.union(current))
unioned.sort()
pages.extend(unioned)
# If there's no overlap between the current set of pages and the last
# set of pages, then there's a possible need for elusion.
if len(current.intersection(last)) == 0:
second_list = list(last)
second_list.sort()
diff = second_list[0] - pages[-1]
# If there is a gap of two, between the last page of the current
# set and the first page of the last set, then we're missing a
# page.
if diff == 2:
pages.append(second_list[0] - 1)
# If the difference is just one, then there's nothing to be done,
# as the pages need no elusion and are correct.
elif diff == 1:
pass
# Otherwise, there's a bigger gap which needs to be signaled for
# elusion, by pushing a None value to the page list.
else:
pages.append(None)
pages.extend(second_list)
else:
differenced = list(last.difference(current))
differenced.sort()
pages.extend(differenced)
to_return = {
'MEDIA_URL': settings.MEDIA_URL,
'request': context['request'],
'pages': pages,
'records': records,
'page_obj': page_obj,
'paginator': paginator,
'hashtag': hashtag,
'is_paginated': paginator.count > paginator.per_page,
'page_suffix': page_suffix,
}
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.inclusion_tag('pagination/pagination.html', takes_context=True)(
paginate)
register.tag('autopaginate', do_autopaginate)
================================================
FILE: pagination/tests.py
================================================
"""
>>> from django.core.paginator import Paginator
>>> from pagination.templatetags.pagination_tags import paginate
>>> from django.template import Template, Context
>>> p = Paginator(range(15), 2)
>>> pg = paginate({'paginator': p, 'page_obj': p.page(1)})
>>> pg['pages']
[1, 2, 3, 4, 5, 6, 7, 8]
>>> pg['records']['first']
1
>>> pg['records']['last']
2
>>> p = Paginator(range(15), 2)
>>> pg = paginate({'paginator': p, 'page_obj': p.page(8)})
>>> pg['pages']
[1, 2, 3, 4, 5, 6, 7, 8]
>>> pg['records']['first']
15
>>> pg['records']['last']
15
>>> p = Paginator(range(17), 2)
>>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages']
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> p = Paginator(range(19), 2)
>>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages']
[1, 2, 3, 4, None, 7, 8, 9, 10]
>>> p = Paginator(range(21), 2)
>>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages']
[1, 2, 3, 4, None, 8, 9, 10, 11]
# Testing orphans
>>> p = Paginator(range(5), 2, 1)
>>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages']
[1, 2]
>>> p = Paginator(range(21), 2, 1)
>>> pg = paginate({'paginator': p, 'page_obj': p.page(1)})
>>> pg['pages']
[1, 2, 3, 4, None, 7, 8, 9, 10]
>>> pg['records']['first']
1
>>> pg['records']['last']
2
>>> p = Paginator(range(21), 2, 1)
>>> pg = paginate({'paginator': p, 'page_obj': p.page(10)})
>>> pg['pages']
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> pg['records']['first']
19
>>> pg['records']['last']
21
>>> p = Paginator(range(21), 2, 1)
>>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages']
[1, 2, 3, 4, None, 7, 8, 9, 10]
>>> t = Template("{% load pagination_tags %}{% autopaginate var 2 %}{% paginate %}")
>>> from django.http import HttpRequest as DjangoHttpRequest
>>> class HttpRequest(DjangoHttpRequest):
... page = lambda self, suffix: 1
>>> t.render(Context({'var': range(21), 'request': HttpRequest()}))
u'\\n\\n<div class="pagination">...
>>>
>>> t = Template("{% load pagination_tags %}{% autopaginate var %}{% paginate %}")
>>> t.render(Context({'var': range(21), 'request': HttpRequest()}))
u'\\n\\n<div class="pagination">...
>>> t = Template("{% load pagination_tags %}{% autopaginate var 20 %}{% paginate %}")
>>> t.render(Context({'var': range(21), 'request': HttpRequest()}))
u'\\n\\n<div class="pagination">...
>>> t = Template("{% load pagination_tags %}{% autopaginate var by %}{% paginate %}")
>>> t.render(Context({'var': range(21), 'by': 20, 'request': HttpRequest()}))
u'\\n\\n<div class="pagination">...<a href="?page=2"...
>>> t = Template("{% load pagination_tags %}{% autopaginate var by as foo %}{{ foo }}")
>>> t.render(Context({'var': range(21), 'by': 20, 'request': HttpRequest()}))
u'[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]'
>>>
>>> t = Template("{% load pagination_tags %}{% autopaginate var2 by as foo2 %}{% paginate %}{% autopaginate var by as foo %}{% paginate %}")
>>> t.render(Context({'var': range(21), 'var2': range(50, 121), 'by': 20, 'request': HttpRequest()}))
u'\\n\\n<div class="pagination">...<a href="?page_var2=2"...<a href="?page_var=2"...
>>>
# Testing InfinitePaginator
>>> from paginator import InfinitePaginator
>>> InfinitePaginator
<class 'pagination.paginator.InfinitePaginator'>
>>> p = InfinitePaginator(range(20), 2, link_template='/bacon/page/%d')
>>> p.validate_number(2)
2
>>> p.orphans
0
>>> p3 = p.page(3)
>>> p3
<Page 3>
>>> p3.end_index()
6
>>> p3.has_next()
True
>>> p3.has_previous()
True
>>> p.page(10).has_next()
False
>>> p.page(1).has_previous()
False
>>> p3.next_link()
'/bacon/page/4'
>>> p3.previous_link()
'/bacon/page/2'
# Testing FinitePaginator
>>> from paginator import FinitePaginator
>>> FinitePaginator
<class 'pagination.paginator.FinitePaginator'>
>>> p = FinitePaginator(range(20), 2, offset=10, link_template='/bacon/page/%d')
>>> p.validate_number(2)
2
>>> p.orphans
0
>>> p3 = p.page(3)
>>> p3
<Page 3>
>>> p3.start_index()
10
>>> p3.end_index()
6
>>> p3.has_next()
True
>>> p3.has_previous()
True
>>> p3.next_link()
'/bacon/page/4'
>>> p3.previous_link()
'/bacon/page/2'
>>> p = FinitePaginator(range(20), 20, offset=10, link_template='/bacon/page/%d')
>>> p2 = p.page(2)
>>> p2
<Page 2>
>>> p2.has_next()
False
>>> p3.has_previous()
True
>>> p2.next_link()
>>> p2.previous_link()
'/bacon/page/1'
>>> from pagination.middleware import PaginationMiddleware
>>> from django.core.handlers.wsgi import WSGIRequest
>>> from StringIO import StringIO
>>> middleware = PaginationMiddleware()
>>> request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart', 'wsgi.input': StringIO()})
>>> middleware.process_request(request)
>>> request.upload_handlers.append('asdf')
"""
================================================
FILE: setup.py
================================================
from setuptools import setup, find_packages
version = '1.0.7'
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.
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.
``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.
``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',
version=version,
description="django-pagination",
long_description=LONG_DESCRIPTION,
classifiers=[
"Programming Language :: Python",
"Topic :: Software Development :: Libraries :: Python Modules",
"Framework :: Django",
"Environment :: Web Environment",
],
keywords='pagination,django',
author='Eric Florenzano',
author_email='floguy@gmail.com',
url='http://django-pagination.googlecode.com/',
license='BSD',
packages=find_packages(),
include_package_data=True,
zip_safe=False,
)
================================================
FILE: tests/runtests.py
================================================
import sys
sys.path.append('..')
import os
# Make a backup of DJANGO_SETTINGS_MODULE environment variable to restore later.
backup = os.environ.get('DJANGO_SETTINGS_MODULE', '')
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from django.test.simple import run_tests
if __name__ == "__main__":
failures = run_tests(['pagination',], verbosity=9)
if failures:
sys.exit(failures)
# Reset the DJANGO_SETTINGS_MODULE to what it was before running tests.
os.environ['DJANGO_SETTINGS_MODULE'] = backup
================================================
FILE: tests/settings.py
================================================
DATABASE_ENGINE = 'sqlite3'
ROOT_URLCONF = ''
SITE_ID = 1
INSTALLED_APPS = (
'pagination',
)
gitextract_lcedyun1/
├── .gitignore
├── CONTRIBUTORS.txt
├── LICENSE.txt
├── MANIFEST.in
├── README.md
├── docs/
│ ├── index.txt
│ ├── install.txt
│ └── usage.txt
├── pagination/
│ ├── __init__.py
│ ├── locale/
│ │ ├── cs/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── de/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── es/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── fr/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── pl/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── pt/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ └── pt_BR/
│ │ └── LC_MESSAGES/
│ │ ├── django.mo
│ │ └── django.po
│ ├── middleware.py
│ ├── models.py
│ ├── paginator.py
│ ├── templates/
│ │ └── pagination/
│ │ └── pagination.html
│ ├── templatetags/
│ │ ├── __init__.py
│ │ └── pagination_tags.py
│ └── tests.py
├── setup.py
└── tests/
├── runtests.py
└── settings.py
SYMBOL INDEX (28 symbols across 3 files)
FILE: pagination/middleware.py
function get_page (line 1) | def get_page(self, suffix):
class PaginationMiddleware (line 11) | class PaginationMiddleware(object):
method process_request (line 16) | def process_request(self, request):
FILE: pagination/paginator.py
class InfinitePaginator (line 3) | class InfinitePaginator(Paginator):
method __init__ (line 13) | def __init__(self, object_list, per_page, allow_empty_first_page=True,
method validate_number (line 23) | def validate_number(self, number):
method page (line 35) | def page(self, number):
method _get_count (line 51) | def _get_count(self):
method _get_num_pages (line 58) | def _get_num_pages(self):
method _get_page_range (line 65) | def _get_page_range(self):
class InfinitePage (line 74) | class InfinitePage(Page):
method __repr__ (line 76) | def __repr__(self):
method has_next (line 79) | def has_next(self):
method end_index (line 90) | def end_index(self):
method next_link (line 100) | def next_link(self):
method previous_link (line 105) | def previous_link(self):
class FinitePaginator (line 110) | class FinitePaginator(InfinitePaginator):
method __init__ (line 128) | def __init__(self, object_list_plus, per_page, offset=None,
method validate_number (line 134) | def validate_number(self, number):
method page (line 144) | def page(self, number):
class FinitePage (line 153) | class FinitePage(InfinitePage):
method has_next (line 155) | def has_next(self):
method start_index (line 165) | def start_index(self):
FILE: pagination/templatetags/pagination_tags.py
function do_autopaginate (line 20) | def do_autopaginate(parser, token):
class AutoPaginateNode (line 62) | class AutoPaginateNode(template.Node):
method __init__ (line 79) | def __init__(self, queryset_var, multiple_paginations, paginate_by=DEF...
method render (line 90) | def render(self, context):
function paginate (line 122) | def paginate(context, window=DEFAULT_WINDOW, hashtag=''):
Condensed preview — 33 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (43K chars).
[
{
"path": ".gitignore",
"chars": 22,
"preview": "*.pyc\ndist\n*.egg-info\n"
},
{
"path": "CONTRIBUTORS.txt",
"chars": 101,
"preview": "Eric Florenzano <floguy@gmail.com>\nMartin Mahner <martin@mahner.org>\nJames Tauber <jtauber@gmail.com>"
},
{
"path": "LICENSE.txt",
"chars": 1521,
"preview": "Copyright (c) 2008, Eric Florenzano\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or wit"
},
{
"path": "MANIFEST.in",
"chars": 172,
"preview": "include LICENSE.txt\ninclude CONTRIBUTORS.txt\nrecursive-include docs *\nrecursive-include pagination/templates/pagination "
},
{
"path": "README.md",
"chars": 131,
"preview": "This project is no longer maintained.\n\nA maintained fork of this project is available at: https://github.com/pydanny/dj-"
},
{
"path": "docs/index.txt",
"chars": 219,
"preview": "#################\ndjango-pagination\n#################\n\nDjango-pagination is a set of utilities for creating robust pagin"
},
{
"path": "docs/install.txt",
"chars": 1366,
"preview": "Installing the latest development version of django-pagination\n---------------------------------------------------------"
},
{
"path": "docs/usage.txt",
"chars": 3812,
"preview": "How to use django-pagination\n----------------------------\n\n``django-pagination`` allows for easy Digg-style pagination w"
},
{
"path": "pagination/__init__.py",
"chars": 1,
"preview": "\n"
},
{
"path": "pagination/locale/cs/LC_MESSAGES/django.po",
"chars": 801,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "pagination/locale/de/LC_MESSAGES/django.po",
"chars": 799,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "pagination/locale/es/LC_MESSAGES/django.po",
"chars": 886,
"preview": "# django-pagination Spanish translation\n# Copyright (C) 2010, Miguel Araujo\n# This file is distributed under the same li"
},
{
"path": "pagination/locale/fr/LC_MESSAGES/django.po",
"chars": 818,
"preview": "# django-pagination French translation.\n# Copyright (C) 2008, Julien Demoor\n# This file is distributed under the same li"
},
{
"path": "pagination/locale/pl/LC_MESSAGES/django.po",
"chars": 824,
"preview": "# Polish translation of django-pagination.\n# Copyright (C) 2008, django-pagination team\n# This file is distributed under"
},
{
"path": "pagination/locale/pt/LC_MESSAGES/django.po",
"chars": 770,
"preview": "# django-pagination Portuguese translation.\n# Copyright (C) 2008, Alcides Fonseca\n# http://alcidesfonseca.com\n# This fil"
},
{
"path": "pagination/locale/pt_BR/LC_MESSAGES/django.po",
"chars": 923,
"preview": "# django-pagination in Portuguese (Brazil).\n# Copyright (C) 2011, Tiago Samaha Cordeiro\n# This file is distributed under"
},
{
"path": "pagination/middleware.py",
"chars": 573,
"preview": "def get_page(self, suffix):\n \"\"\"\n A function which will be monkeypatched onto the request to get the current\n i"
},
{
"path": "pagination/models.py",
"chars": 1,
"preview": "\n"
},
{
"path": "pagination/paginator.py",
"chars": 5771,
"preview": "from django.core.paginator import Paginator, Page, PageNotAnInteger, EmptyPage\n\nclass InfinitePaginator(Paginator):\n "
},
{
"path": "pagination/templates/pagination/pagination.html",
"chars": 1080,
"preview": "{% if is_paginated %}\n{% load i18n %}\n<div class=\"pagination\">\n {% if page_obj.has_previous %}\n <a href=\"?page"
},
{
"path": "pagination/templatetags/__init__.py",
"chars": 1,
"preview": "\n"
},
{
"path": "pagination/templatetags/pagination_tags.py",
"chars": 10303,
"preview": "try:\n set\nexcept NameError:\n from sets import Set as set\n\nfrom django import template\nfrom django.template import "
},
{
"path": "pagination/tests.py",
"chars": 4683,
"preview": "\"\"\"\n>>> from django.core.paginator import Paginator\n>>> from pagination.templatetags.pagination_tags import paginate\n>>>"
},
{
"path": "setup.py",
"chars": 4108,
"preview": "from setuptools import setup, find_packages\n\nversion = '1.0.7'\n\nLONG_DESCRIPTION = \"\"\"\nHow to use django-pagination\n----"
},
{
"path": "tests/runtests.py",
"chars": 524,
"preview": "import sys\nsys.path.append('..')\n\nimport os\n# Make a backup of DJANGO_SETTINGS_MODULE environment variable to restore la"
},
{
"path": "tests/settings.py",
"chars": 97,
"preview": "DATABASE_ENGINE = 'sqlite3'\nROOT_URLCONF = ''\nSITE_ID = 1\nINSTALLED_APPS = (\n 'pagination',\n)\n"
}
]
// ... and 7 more files (download for full content)
About this extraction
This page contains the full source code of the ericflo/django-pagination GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 33 files (39.4 KB), approximately 10.7k tokens, and a symbol index with 28 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.