Pagination

flask-mongoengine attaches the following methods to Mongoengine’s default QuerySet:

  • paginate: paginates the QuerySet. Takes two required arguments, page and per_page, and two optional arguments: max_depth and first_page_index.

  • paginate_by_keyset: paginates the QuerySet. Takes two required arguments, per_page and field_filter_by.

  • From the second page, you need the last ID of the previous page. Arguments: per_page, field_filter_by, last_field_value.

  • paginate_field: paginates a field from one document in the QuerySet. Arguments: field_name, doc_id, page, per_page,

  • and two optional arguments: total, first_page_index.

Offset Pagination

Offset API pagination, sometimes called page-based pagination, is the most common form of API pagination. An API endpoint accepts parameters for page number and items per page, then returns the specified page values.

Example 1

from flask import request

page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 10, type=int)

paginated_todos = Todo.objects.paginate(page=page, per_page=per_page)

Example 2

from flask import request
from flask_mongoengine import Pagination

page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 10, type=int)

Pagination(Todo.objects, page=page, per_page=per_page)

Keyset pagination

Keyset-based API pagination is when an API provides a key parameter that delineates the query results. One example is if an API is sorted by ID, one key parameter could be since_id.

Keyset pagination is a great way to paginate through a large dataset without having to worry about the page number. It also allows for a more consistent user experience as the user can always see the same results when they go to the next page.

Example 1

from flask import request

field_filter_by = '_id'
per_page = request.args.get('per_page', 10, type=int)

paginated_todos = Todo.objects.paginate_by_keyset(per_page=per_page, field_filter_by=field_filter_by)

# PAGE 2
paginated_todos_2 = Todo.objects.paginate_by_keyset(per_page=per_page, field_filter_by=field_filter_by,
                                                    last_field_value='value_of last page')

Example 2

from flask import request
from flask_mongoengine import KeysetPagination

field_filter_by = '_id'
per_page = request.args.get('per_page', 10, type=int)


KeysetPagination(Todo.objects, per_page=per_page, field_filter_by=field_filter_by)

List field pagination

List field pagination is when an API provides a key parameter that is the field name that is list of values and paginate on this list.

Example 1

from flask import request

page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 10, type=int)
todo_id = request.args.get('todo_id', 10)

todo = Todo.objects.get_or_404(_id=todo_id)
paginated_tags = todo.paginate_field(field_name='tags', page=page, per_page=per_page)

Example 2

from flask import request

page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 10, type=int)
todo_id = request.args.get('todo_id', 10)


paginated_tags = Todo.paginate_field(Todo.objects, doc_id=todo_id, field_name='tags',
                                     page=page, per_page=per_page)

Example 3

from flask import request
from flask_mongoengine import ListFieldPagination

page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 10, type=int)
todo_id = request.args.get('todo_id', 10)


ListFieldPagination(Todo.objects, doc_id=todo_id, field_name='tags',
                    page=page, per_page=per_page)

Render page with pagination

Properties of the pagination object include: iter_pages, next, prev, has_next, has_prev, next_num, prev_num.

In the template:

{# Display a page of todos #}
<ul>
    {% for todo in paginated_todos.items %}
        <li>{{ todo.title }}</li>
    {% endfor %}
</ul>

{# Macro for creating navigation links #}
{% macro render_navigation(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 %}

{{ render_navigation(paginated_todos, 'view_todos') }}