Skip to content
This repository was archived by the owner on Jan 7, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions sandbox/manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_pagination.settings")
try:
from django.core.management import execute_from_command_line
except ImportError:
# The above import may fail for some other reason. Ensure that the
# issue is really that Django is missing to avoid masking other
# exceptions on Python 2.
try:
import django
except ImportError:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
)
raise
execute_from_command_line(sys.argv)
2 changes: 2 additions & 0 deletions sandbox/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Django==1.11.2
django-simple-pagination==1.1.8
Empty file added sandbox/sample/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions sandbox/sample/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.contrib import admin

# Register your models here.
8 changes: 8 additions & 0 deletions sandbox/sample/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.apps import AppConfig


class SampleConfig(AppConfig):
name = 'sample'
Empty file.
6 changes: 6 additions & 0 deletions sandbox/sample/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models

# Create your models here.
6 changes: 6 additions & 0 deletions sandbox/sample/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.test import TestCase

# Create your tests here.
11 changes: 11 additions & 0 deletions sandbox/sample/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.shortcuts import render
from django.contrib.auth.models import User


# Create your views here.
def users(request):
users = User.objects.all()
return render(request, 'users.html', {'users': users})
Empty file.
Empty file.
Empty file.
290 changes: 290 additions & 0 deletions sandbox/simple_pagination/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
"""Ephemeral models used to represent a page and a list of pages."""

from __future__ import unicode_literals

from django.template import loader
from django.utils.encoding import iri_to_uri

from simple_pagination import settings
from simple_pagination import utils


# Page templates cache.
_template_cache = {}


class EndlessPage(utils.UnicodeMixin):
"""A page link representation.

Interesting attributes:

- *self.number*: the page number;
- *self.label*: the label of the link
(usually the page number as string);
- *self.url*: the url of the page (strting with "?");
- *self.path*: the path of the page;
- *self.is_current*: return True if page is the current page displayed;
- *self.is_first*: return True if page is the first page;
- *self.is_last*: return True if page is the last page.
"""

def __init__(self, request, number, current_number, *args, **kwargs):
total_number = kwargs.get('total_number')
querystring_key = kwargs.get('querystring_key', 'page')
label = kwargs.get('label', None)
default_number = kwargs.get('default_number', 1)
override_path = kwargs.get('override_path', None)
self._request = request
self.number = number
self.label = utils.text(number) if label is None else label
self.querystring_key = querystring_key

self.is_current = number == current_number
self.is_first = number == 1
self.is_last = number == total_number

self.url = utils.get_querystring_for_page(
request, number, self.querystring_key,
default_number=default_number)
path = iri_to_uri(override_path or request.path)
self.path = '{0}{1}'.format(path, self.url)

def __unicode__(self):
"""Render the page as a link."""
context = {
'add_nofollow': False,
'page': self,
'querystring_key': self.querystring_key,
}
if self.is_current:
template_name = 'simple/current_link.html'
else:
template_name = 'simple/page_link.html'
template = _template_cache.setdefault(
template_name, loader.get_template(template_name))
return template.render(context)


class PageList(utils.UnicodeMixin):
"""A sequence of endless pages."""

def __init__(self, request, page, querystring_key, **kwargs):
default_number = kwargs.get('default_number', None)
override_path = kwargs.get('override_path', None)
self._request = request
self._page = page
if default_number is None:
self._default_number = 1
else:
self._default_number = int(default_number)
self._querystring_key = querystring_key
self._override_path = override_path

def _endless_page(self, number, label=None):
"""Factory function that returns a *EndlessPage* instance.

This method works just like a partial constructor.
"""
return EndlessPage(
self._request,
number,
self._page.number,
len(self),
self._querystring_key,
label=label,
default_number=self._default_number,
override_path=self._override_path,
)

def __getitem__(self, value):
# The type conversion is required here because in templates Django
# performs a dictionary lookup before the attribute lokups
# (when a dot is encountered).
try:
value = int(value)
except (TypeError, ValueError):
# A TypeError says to django to continue with an attribute lookup.
raise TypeError
if 1 <= value <= len(self):
return self._endless_page(value)
raise IndexError('page list index out of range')

def __len__(self):
"""The length of the sequence is the total number of pages."""
return self._page.paginator.num_pages

def __iter__(self):
"""Iterate over all the endless pages (from first to last)."""
for i in range(len(self)):
yield self[i + 1]

def __unicode__(self):
"""Return a rendered Digg-style pagination (by default).

The callable *settings.PAGE_LIST_CALLABLE* can be used to customize
how the pages are displayed. The callable takes the current page number
and the total number of pages, and must return a sequence of page
numbers that will be displayed. The sequence can contain other values:

- *'previous'*: will display the previous page in that position;
- *'next'*: will display the next page in that position;
- *'first'*: will display the first page as an arrow;
- *'last'*: will display the last page as an arrow;
- *None*: a separator will be displayed in that position.

Here is an example of custom calable that displays the previous page,
then the first page, then a separator, then the current page, and
finally the last page::

def get_page_numbers(current_page, num_pages):
return ('previous', 1, None, current_page, 'last')

If *settings.PAGE_LIST_CALLABLE* is None an internal callable is used,
generating a Digg-style pagination. The value of
*settings.PAGE_LIST_CALLABLE* can also be a dotted path to a callable.
"""
if len(self) > 1:
pages_callable = utils.get_page_numbers
pages = []
for item in pages_callable(self._page.number, len(self)):
if item is None:
pages.append(None)
elif item == 'previous':
pages.append(self.previous())
elif item == 'next':
pages.append(self.next())
elif item == 'first':
pages.append(self.first_as_arrow())
elif item == 'last':
pages.append(self.last_as_arrow())
else:
pages.append(self[item])
return loader.render_to_string('simple/show_pages.html', {'pages': pages})
return ''

def current(self):
"""Return the current page."""
return self._endless_page(self._page.number)

def current_start_index(self):
"""Return the 1-based index of the first item on the current page."""
return self._page.start_index()

def current_end_index(self):
"""Return the 1-based index of the last item on the current page."""
return self._page.end_index()

def total_count(self):
"""Return the total number of objects, across all pages."""
return self._page.paginator.count

def first(self, label=None):
"""Return the first page."""
return self._endless_page(1, label=label)

def last(self, label=None):
"""Return the last page."""
return self._endless_page(len(self), label=label)

def first_as_arrow(self):
"""Return the first page as an arrow.

The page label (arrow) is defined in ``settings.FIRST_LABEL``.
"""
return self.first(label=settings.FIRST_LABEL)

def last_as_arrow(self):
"""Return the last page as an arrow.

The page label (arrow) is defined in ``settings.LAST_LABEL``.
"""
return self.last(label=settings.LAST_LABEL)

def previous(self):
"""Return the previous page.

The page label is defined in ``settings.PREVIOUS_LABEL``.
Return an empty string if current page is the first.
"""
if self._page.has_previous():
return self._endless_page(
self._page.previous_page_number(),
label=settings.PREVIOUS_LABEL)
return ''

def next(self):
"""Return the next page.

The page label is defined in ``settings.NEXT_LABEL``.
Return an empty string if current page is the last.
"""
if self._page.has_next():
return self._endless_page(
self._page.next_page_number(),
label=settings.NEXT_LABEL)
return ''

def paginated(self):
"""Return True if this page list contains more than one page."""
return len(self) > 1


class ShowItems(utils.UnicodeMixin):
"""A page link representation.

Interesting attributes:

- *self.number*: the page number;
- *self.label*: the label of the link
(usually the page number as string);
- *self.url*: the url of the page (strting with "?");
- *self.path*: the path of the page;
- *self.is_current*: return True if page is the current page displayed;
- *self.is_first*: return True if page is the first page;
- *self.is_last*: return True if page is the last page.
"""

def __init__(self, request, page, querystring_key, **kwargs):
default_number = kwargs.get('default_number', None)
override_path = kwargs.get('override_path', None)
self._request = request
self._page = page
if default_number is None:
self._default_number = 1
else:
self._default_number = int(default_number)
self._querystring_key = querystring_key
self._override_path = override_path

def __unicode__(self):
"""Render the page as a link."""
str_data = "Showing "
if self._page.paginator.count == 1:
str_data += str(1)
str_data = str_data + " to " + str(len(self._page.object_list)) + " of " + str(len(self._page.object_list))
else:
if self._page.number == 1:
str_data += str(1)
if self._page.paginator.per_page == str(self._page.paginator.count):
str_data = str_data + " to " + str(self._page.paginator.per_page) + " of " + str(self._page.paginator.count)
else:
str_data = str_data + " to " + str(len(self._page.object_list)) + " of " + str(self._page.paginator.count)
else:
if self._page.has_next():
str_data += "".join(map(str, [
(self._page.paginator.per_page * self._page.previous_page_number()) + 1,
" to ",
self._page.paginator.per_page * self._page.number,
" of ",
self._page.paginator.count
]))
else:
str_data += "".join(map(str, [
self._page.paginator.per_page * self._page.previous_page_number() + 1,
" to ",
self._page.paginator.count,
" of ",
self._page.paginator.count
]))

return str_data + " items"
12 changes: 12 additions & 0 deletions sandbox/simple_pagination/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from django.conf import settings

PER_PAGE = getattr(settings, 'SIMPLE_PAGINATION_PER_PAGE', 10)
PAGE_LABEL = getattr(settings, 'SIMPLE_PAGINATION_PAGE_LABEL', 'page')
NEXT_LABEL = getattr(
settings, 'SIMPLE_PAGINATION_NEXT_LABEL', '<span aria-hidden="true">&gt;</span>')
PREVIOUS_LABEL = getattr(
settings, 'SIMPLE_PAGINATION_PREVIOUS_LABEL', '<span aria-hidden="true">&lt;</span>')
LAST_LABEL = getattr(
settings, 'SIMPLE_PAGINATION_LAST_LABEL', '<span aria-hidden="true">&gt;&gt;</span>')
FIRST_LABEL = getattr(
settings, 'SIMPLE_PAGINATION_FIRST_LABEL', '<span aria-hidden="true">&lt;&lt;</span>')
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<li><a class="active">{{ page.label|safe }}</a></li>
1 change: 1 addition & 0 deletions sandbox/simple_pagination/templates/simple/page_link.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<li><a href="{{ page.path }}" rel="{{ querystring_key }}{% if add_nofollow %} nofollow{% endif %}">{{ page.label|safe }}</a></li>
3 changes: 3 additions & 0 deletions sandbox/simple_pagination/templates/simple/show_pages.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<ul class="pagination">
{% for page in pages %}{{page}}{% endfor %}
</ul>
Empty file.
Loading