|
@@ -1,142 +0,0 @@
|
|
|
-# -*- coding: utf-8 -*-
|
|
|
-"""
|
|
|
- flaskbb.utils.query
|
|
|
- ~~~~~~~~~~~~~~~~~~~
|
|
|
-
|
|
|
- This module holds the query classes that are used by flaskbb
|
|
|
-
|
|
|
- :copyright: (c) 2014 by the FlaskBB Team.
|
|
|
- :license: BSD, see LICENSE for more details.
|
|
|
-"""
|
|
|
-from math import ceil
|
|
|
-
|
|
|
-from flask import abort
|
|
|
-from flask.ext.sqlalchemy import BaseQuery
|
|
|
-
|
|
|
-
|
|
|
-class Pagination(object):
|
|
|
- """Internal helper class returned by :meth:`BaseQuery.paginate`. You
|
|
|
- can also construct it from any other SQLAlchemy query object if you are
|
|
|
- working with other libraries. Additionally it is possible to pass `None`
|
|
|
- as query object in which case the :meth:`prev` and :meth:`next` will
|
|
|
- no longer work.
|
|
|
- """
|
|
|
-
|
|
|
- def __init__(self, query, page, per_page, total, items, add_none):
|
|
|
- #: the unlimited query object that was used to create this
|
|
|
- #: pagination object.
|
|
|
- self.query = query
|
|
|
- #: the current page number (1 indexed)
|
|
|
- self.page = page
|
|
|
- #: the number of items to be displayed on a page.
|
|
|
- self.per_page = per_page
|
|
|
- #: the total number of items matching the query
|
|
|
- self.total = total
|
|
|
- #: the items for the current page
|
|
|
- if add_none:
|
|
|
- self.items = [(item, None) for item in items]
|
|
|
- else:
|
|
|
- self.items = items
|
|
|
-
|
|
|
- @property
|
|
|
- def pages(self):
|
|
|
- """The total number of pages"""
|
|
|
- if self.per_page == 0:
|
|
|
- pages = 0
|
|
|
- else:
|
|
|
- pages = int(ceil(self.total / float(self.per_page)))
|
|
|
- return pages
|
|
|
-
|
|
|
- def prev(self, error_out=False, add_none=False):
|
|
|
- """Returns a :class:`Pagination` object for the previous page."""
|
|
|
- assert self.query is not None, 'a query object is required ' \
|
|
|
- 'for this method to work'
|
|
|
- return self.query.paginate(self.page - 1, self.per_page, error_out,
|
|
|
- self.add_none)
|
|
|
-
|
|
|
- @property
|
|
|
- def prev_num(self):
|
|
|
- """Number of the previous page."""
|
|
|
- return self.page - 1
|
|
|
-
|
|
|
- @property
|
|
|
- def has_prev(self):
|
|
|
- """True if a previous page exists"""
|
|
|
- return self.page > 1
|
|
|
-
|
|
|
- def next(self, error_out=False, add_none=False):
|
|
|
- """Returns a :class:`Pagination` object for the next page."""
|
|
|
- assert self.query is not None, 'a query object is required ' \
|
|
|
- 'for this method to work'
|
|
|
- return self.query.paginate(self.page + 1, self.per_page, error_out,
|
|
|
- self.add_none)
|
|
|
-
|
|
|
- @property
|
|
|
- def has_next(self):
|
|
|
- """True if a next page exists."""
|
|
|
- return self.page < self.pages
|
|
|
-
|
|
|
- @property
|
|
|
- def next_num(self):
|
|
|
- """Number of the next page"""
|
|
|
- return self.page + 1
|
|
|
-
|
|
|
- def iter_pages(self, left_edge=2, left_current=2,
|
|
|
- right_current=5, right_edge=2):
|
|
|
- """Iterates over the page numbers in the pagination. The four
|
|
|
- parameters control the thresholds how many numbers should be produced
|
|
|
- from the sides. Skipped page numbers are represented as `None`.
|
|
|
- This is how you could render such a pagination in the templates:
|
|
|
-
|
|
|
- .. sourcecode:: html+jinja
|
|
|
-
|
|
|
- {% macro render_pagination(pagination, endpoint) %}
|
|
|
- <div class=pagination>
|
|
|
- {%- for page in pagination.iter_pages() %}
|
|
|
- {% if page %}
|
|
|
- {% if page != pagination.page %}
|
|
|
- <a href="{{ url_for(endpoint, page=page) }}">{{ page }}</a>
|
|
|
- {% else %}
|
|
|
- <strong>{{ page }}</strong>
|
|
|
- {% endif %}
|
|
|
- {% else %}
|
|
|
- <span class=ellipsis>…</span>
|
|
|
- {% endif %}
|
|
|
- {%- endfor %}
|
|
|
- </div>
|
|
|
- {% endmacro %}
|
|
|
- """
|
|
|
- last = 0
|
|
|
- for num in xrange(1, self.pages + 1):
|
|
|
- if num <= left_edge or \
|
|
|
- (num > self.page - left_current - 1 and
|
|
|
- num < self.page + right_current) or \
|
|
|
- num > self.pages - right_edge:
|
|
|
- if last + 1 != num:
|
|
|
- yield None
|
|
|
- yield num
|
|
|
- last = num
|
|
|
-
|
|
|
-
|
|
|
-class TopicQuery(BaseQuery):
|
|
|
- def paginate(self, page, per_page=20, error_out=True, add_none=False):
|
|
|
- """Returns `per_page` items from page `page`. By default it will
|
|
|
- abort with 404 if no items were found and the page was larger than
|
|
|
- 1. This behavor can be disabled by setting `error_out` to `False`.
|
|
|
-
|
|
|
- Returns an :class:`Pagination` object.
|
|
|
- """
|
|
|
- if error_out and page < 1:
|
|
|
- abort(404)
|
|
|
- items = self.limit(per_page).offset((page - 1) * per_page).all()
|
|
|
- if not items and page != 1 and error_out:
|
|
|
- abort(404)
|
|
|
-
|
|
|
- # No need to count if we're on the first page and there are fewer
|
|
|
- # items than we expected.
|
|
|
- if page == 1 and len(items) < per_page:
|
|
|
- total = len(items)
|
|
|
- else:
|
|
|
- total = self.order_by(None).count()
|
|
|
-
|
|
|
- return Pagination(self, page, per_page, total, items, add_none)
|