Skip to content
Draft
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
4 changes: 4 additions & 0 deletions django_tables2/columns/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,10 @@ def order_by_alias(self):
order_by.next = order_by.opposite
return order_by

@property
def order_by_query(self):
return {self._table.prefixed_order_by_field: self.order_by_alias.next}

@property
def is_ordered(self):
return self.name in (self._table.order_by or ())
Expand Down
76 changes: 76 additions & 0 deletions django_tables2/paginators.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,79 @@
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.paginator import EmptyPage, Page, PageNotAnInteger, Paginator
from django.utils.translation import gettext as _


class PageNumber(int):
def __new__(cls, number, prefixed_page_field):
self = super().__new__(cls, number)
self._prefixed_page_field = prefixed_page_field
return self

@property
def query(self):
return {self._prefixed_page_field: str(self)}


class TablePaginatorMixin:
def __init__(self, *args, **kwargs):
table = kwargs.pop("table", None)
super().__init__(*args, **kwargs)
self.table = table
self._prefixed_page_field = self.table.prefixed_page_field if self.table else "page"

def previous_page_query(self):
return {self._prefixed_page_field: self.table.page.previous_page_number()}

def next_page_query(self):
return {self._prefixed_page_field: self.table.page.next_page_number()}

def page_query(self, page_number):
return {self._prefixed_page_field: page_number}

def table_page_range(self):
"""
Return a list of max 10 (by default) page numbers.

- always containing the first, last and current page.
- containing one or two '...' to skip ranges between first/last and current.

Example:
{% for p in table.paginator.table_page_range %}
{{ p }}
{% endfor %}
"""
if self.table is None:
raise ImproperlyConfigured("table_page_range requires table to be set")

page_range = getattr(settings, "DJANGO_TABLES2_PAGE_RANGE", 10)

num_pages = self.num_pages
if num_pages <= page_range:
return [PageNumber(i, self._prefixed_page_field) for i in range(1, num_pages + 1)]

range_start = self.table.page.number - int(page_range / 2)
if range_start < 1:
range_start = 1
range_end = range_start + page_range
if range_end > num_pages:
range_start = num_pages - page_range + 1
range_end = num_pages + 1

ret = range(range_start, range_end)
if 1 not in ret:
ret = [1, Paginator.ELLIPSIS, *ret[2:]]
if num_pages not in ret:
ret = [*ret[:-2], Paginator.ELLIPSIS, num_pages]
if isinstance(self, LazyPaginator) and not self.is_last_page(self.table.page.number):
ret.append(Paginator.ELLIPSIS)
return [PageNumber(i, self._prefixed_page_field) if isinstance(i, int) else i for i in ret]


class TablePaginator(TablePaginatorMixin, Paginator):
pass


class LazyPaginator(Paginator):
"""
Implement lazy pagination, preventing any count() queries.
Expand Down Expand Up @@ -115,3 +187,7 @@ def _get_page_range(self):
raise NotImplementedError

page_range = property(_get_page_range)


class LazyTablePaginator(TablePaginatorMixin, LazyPaginator):
pass
6 changes: 4 additions & 2 deletions django_tables2/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
from itertools import count

from django.conf import settings
from django.core.paginator import Paginator
from django.db import models
from django.template.loader import get_template
from django.utils.encoding import force_str

from .columns import BoundColumns, Column, library
from .config import RequestConfig
from .data import TableData
from .paginators import TablePaginator, TablePaginatorMixin
from .rows import BoundRows
from .utils import Accessor, AttributeDict, OrderBy, OrderByTuple, Sequence

Expand Down Expand Up @@ -549,7 +549,7 @@ def page_field(self):
def page_field(self, value):
self._page_field = value

def paginate(self, paginator_class=Paginator, per_page=None, page=1, *args, **kwargs):
def paginate(self, paginator_class=TablePaginator, per_page=None, page=1, *args, **kwargs):
"""
Paginate the table using a paginator and creates a `page` property containing information for the current page.

Expand All @@ -566,6 +566,8 @@ def paginate(self, paginator_class=Paginator, per_page=None, page=1, *args, **kw
may be raised from this method and should be handled by the caller.
"""
per_page = per_page or self._meta.per_page
if issubclass(paginator_class, TablePaginatorMixin):
kwargs["table"] = self
self.paginator = paginator_class(self.rows, per_page, *args, **kwargs)
self.page = self.paginator.page(page)

Expand Down
10 changes: 5 additions & 5 deletions django_tables2/templates/django_tables2/bootstrap.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{% for column in table.columns %}
<th {{ column.attrs.th.as_html }}>
{% if column.orderable %}
<a href="{% querystring_replace table.prefixed_order_by_field=column.order_by_alias.next %}">{{ column.header }}</a>
<a href="{% querystring column.order_by_query %}">{{ column.header }}</a>
{% else %}
{{ column.header }}
{% endif %}
Expand Down Expand Up @@ -61,7 +61,7 @@
{% if table.page.has_previous %}
{% block pagination.previous %}
<li class="previous">
<a href="{% querystring_replace table.prefixed_page_field=table.page.previous_page_number %}">
<a href="{% querystring table.paginator.previous_page_query %}">
<span aria-hidden="true">&laquo;</span>
{% trans 'previous' %}
</a>
Expand All @@ -70,12 +70,12 @@
{% endif %}
{% if table.page.has_previous or table.page.has_next %}
{% block pagination.range %}
{% for p in table.page|table_page_range:table.paginator %}
{% for p in table.paginator.table_page_range %}
<li {% if p == table.page.number %}class="active"{% endif %}>
{% if p == '...' %}
<a href="#">{{ p }}</a>
{% else %}
<a href="{% querystring_replace table.prefixed_page_field=p %}">
<a href="{% querystring p.query %}">
{{ p }}
</a>
{% endif %}
Expand All @@ -87,7 +87,7 @@
{% if table.page.has_next %}
{% block pagination.next %}
<li class="next">
<a href="{% querystring_replace table.prefixed_page_field=table.page.next_page_number %}">
<a href="{% querystring table.paginator.next_page_query %}">
{% trans 'next' %}
<span aria-hidden="true">&raquo;</span>
</a>
Expand Down
10 changes: 5 additions & 5 deletions django_tables2/templates/django_tables2/bootstrap4.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{% for column in table.columns %}
<th {{ column.attrs.th.as_html }}>
{% if column.orderable %}
<a href="{% querystring_replace table.prefixed_order_by_field=column.order_by_alias.next %}">{{ column.header }}</a>
<a href="{% querystring column.order_by_query %}">{{ column.header }}</a>
{% else %}
{{ column.header }}
{% endif %}
Expand Down Expand Up @@ -61,7 +61,7 @@
{% if table.page.has_previous %}
{% block pagination.previous %}
<li class="previous page-item">
<a href="{% querystring_replace table.prefixed_page_field=table.page.previous_page_number %}" class="page-link">
<a href="{% querystring table.paginator.previous_page_query %}" class="page-link">
<span aria-hidden="true">&laquo;</span>
{% trans 'previous' %}
</a>
Expand All @@ -70,9 +70,9 @@
{% endif %}
{% if table.page.has_previous or table.page.has_next %}
{% block pagination.range %}
{% for p in table.page|table_page_range:table.paginator %}
{% for p in table.paginator.table_page_range %}
<li class="page-item{% if table.page.number == p %} active{% endif %}">
<a class="page-link" {% if p != '...' %}href="{% querystring_replace table.prefixed_page_field=p %}"{% endif %}>
<a class="page-link" {% if p != '...' %}href="{% querystring p.query %}"{% endif %}>
{{ p }}
</a>
</li>
Expand All @@ -82,7 +82,7 @@
{% if table.page.has_next %}
{% block pagination.next %}
<li class="next page-item">
<a href="{% querystring_replace table.prefixed_page_field=table.page.next_page_number %}" class="page-link">
<a href="{% querystring table.paginator.next_page_query %}" class="page-link">
{% trans 'next' %}
<span aria-hidden="true">&raquo;</span>
</a>
Expand Down
10 changes: 5 additions & 5 deletions django_tables2/templates/django_tables2/bootstrap5.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{% for column in table.columns %}
<th {{ column.attrs.th.as_html }} scope="col">
{% if column.orderable %}
<a href="{% querystring_replace table.prefixed_order_by_field=column.order_by_alias.next %}">{{ column.header }}</a>
<a href="{% querystring column.order_by_query %}">{{ column.header }}</a>
{% else %}
{{ column.header }}
{% endif %}
Expand Down Expand Up @@ -61,7 +61,7 @@
{% if table.page.has_previous %}
{% block pagination.previous %}
<li class="previous page-item">
<a href="{% querystring_replace table.prefixed_page_field=table.page.previous_page_number %}" class="page-link">
<a href="{% querystring table.paginator.previous_page_query %}" class="page-link">
<span aria-hidden="true">&laquo;</span>
{% trans 'previous' %}
</a>
Expand All @@ -70,9 +70,9 @@
{% endif %}
{% if table.page.has_previous or table.page.has_next %}
{% block pagination.range %}
{% for p in table.page|table_page_range:table.paginator %}
{% for p in table.paginator.table_page_range %}
<li class="page-item{% if table.page.number == p %} active{% endif %}">
<a class="page-link" {% if p != '...' %}href="{% querystring_replace table.prefixed_page_field=p %}"{% endif %}>
<a class="page-link" {% if p != '...' %}href="{% querystring p.query %}"{% endif %}>
{{ p }}
</a>
</li>
Expand All @@ -82,7 +82,7 @@
{% if table.page.has_next %}
{% block pagination.next %}
<li class="next page-item">
<a href="{% querystring_replace table.prefixed_page_field=table.page.next_page_number %}" class="page-link">
<a href="{% querystring table.paginator.next_page_query %}" class="page-link">
{% trans 'next' %}
<span aria-hidden="true">&raquo;</span>
</a>
Expand Down
10 changes: 5 additions & 5 deletions django_tables2/templates/django_tables2/semantic.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{% for column in table.columns %}
<th {{ column.attrs.th.as_html }}>
{% if column.orderable %}
<a href="{% querystring_replace table.prefixed_order_by_field=column.order_by_alias.next %}">{{ column.header }}</a>
<a href="{% querystring column.order_by_query %}">{{ column.header }}</a>
{% else %}
{{ column.header }}
{% endif %}
Expand Down Expand Up @@ -56,19 +56,19 @@
<div class="ui right floated pagination menu">
{% if table.page.has_previous %}
{% block pagination.previous %}
<a href="{% querystring_replace table.prefixed_page_field=table.page.previous_page_number %}" class="icon item">
<a href="{% querystring table.paginator.previous_page_query %}" class="icon item">
<i class="left chevron icon"></i>
</a>
{% endblock pagination.previous %}
{% endif %}

{% if table.page.has_previous or table.page.has_next %}
{% block pagination.range %}
{% for p in table.page|table_page_range:table.paginator %}
{% for p in table.paginator.table_page_range %}
{% if p == '...' %}
<a href="#" class="item">{{ p }}</a>
{% else %}
<a href="{% querystring_replace table.prefixed_page_field=p %}" class="item {% if p == table.page.number %}active{% endif %}">
<a href="{% querystring p.query %}" class="item {% if p == table.page.number %}active{% endif %}">
{{ p }}
</a>
{% endif %}
Expand All @@ -78,7 +78,7 @@

{% if table.page.has_next %}
{% block pagination.next %}
<a href="{% querystring_replace table.prefixed_page_field=table.page.next_page_number %}" class="icon item">
<a href="{% querystring table.paginator.next_page_query %}" class="icon item">
<i class="right chevron icon"></i>
</a>
{% endblock pagination.next %}
Expand Down
10 changes: 5 additions & 5 deletions django_tables2/templates/django_tables2/table.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{% for column in table.columns %}
<th {{ column.attrs.th.as_html }}>
{% if column.orderable %}
<a href="{% querystring_replace table.prefixed_order_by_field=column.order_by_alias.next %}">{{ column.header }}</a>
<a href="{% querystring column.order_by_query %}">{{ column.header }}</a>
{% else %}
{{ column.header }}
{% endif %}
Expand Down Expand Up @@ -60,20 +60,20 @@
{% if table.page.has_previous %}
{% block pagination.previous %}
<li class="previous">
<a href="{% querystring_replace table.prefixed_page_field=table.page.previous_page_number %}">
<a href="{% querystring table.paginator.previous_page_query %}">
{% trans 'previous' %}
</a>
</li>
{% endblock pagination.previous %}
{% endif %}
{% if table.page.has_previous or table.page.has_next %}
{% block pagination.range %}
{% for p in table.page|table_page_range:table.paginator %}
{% for p in table.paginator.table_page_range %}
<li {% if p == table.page.number %}class="active"{% endif %}>
{% if p == '...' %}
<a href="#">{{ p }}</a>
{% else %}
<a href="{% querystring_replace table.prefixed_page_field=p %}">
<a href="{% querystring p.query %}">
{{ p }}
</a>
{% endif %}
Expand All @@ -84,7 +84,7 @@
{% if table.page.has_next %}
{% block pagination.next %}
<li class="next">
<a href="{% querystring_replace table.prefixed_page_field=table.page.next_page_number %}">
<a href="{% querystring table.paginator.next_page_query %}">
{% trans 'next' %}
</a>
</li>
Expand Down
39 changes: 0 additions & 39 deletions django_tables2/templatetags/django_tables2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from collections import OrderedDict

from django import template
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.template import Node, TemplateSyntaxError
from django.template.loader import get_template, select_template
Expand All @@ -11,7 +10,6 @@
from django.utils.http import urlencode

import django_tables2 as tables
from django_tables2.paginators import LazyPaginator
from django_tables2.utils import AttributeDict

register = template.Library()
Expand Down Expand Up @@ -235,43 +233,6 @@ def export_url(context, export_format, export_trigger_param=None):
).render(context)


@register.filter
def table_page_range(page, paginator):
"""
Given an page and paginator, return a list of max 10 (by default) page numbers.

- always containing the first, last and current page.
- containing one or two '...' to skip ranges between first/last and current.

Example:
{% for p in table.page|table_page_range:table.paginator %}
{{ p }}
{% endfor %}
"""
page_range = getattr(settings, "DJANGO_TABLES2_PAGE_RANGE", 10)

num_pages = paginator.num_pages
if num_pages <= page_range:
return range(1, num_pages + 1)

range_start = page.number - int(page_range / 2)
if range_start < 1:
range_start = 1
range_end = range_start + page_range
if range_end > num_pages:
range_start = num_pages - page_range + 1
range_end = num_pages + 1

ret = range(range_start, range_end)
if 1 not in ret:
ret = [1, "..."] + list(ret)[2:]
if num_pages not in ret:
ret = list(ret)[:-2] + ["...", num_pages]
if isinstance(paginator, LazyPaginator) and not paginator.is_last_page(page.number):
ret.append("...")
return ret


@register.simple_tag
def render_attrs(attrs, **kwargs):
ret = AttributeDict(kwargs)
Expand Down
Loading