NDB has very useful query.fetch_page() method that does most of work for you. It can be used like this:

query = Article.query()

articles_for_page1, cursor, more = query.fetch_page(1)

if more:
    articles_for_page2, cursor, more = query.fetch_page(2, cursor=cursor)

Having that method the only thing developer should do is storing cursor somewhere. There are several places where you can store it:

  1. Use cursor.to_websafe_string() and pass it via URL.
  2. Store cursor in session just for one user.
  3. Store cursor in memcache for all users.

ndbpager caches cursor for developer in memcache. Usage:

import ndbpager

pager = ndbpager.Pager(query=query, page=1)
articles_for_page1, _, _ = pager.paginate(page_size=20)

Internally ndbpager checks memcache for cursor for the given page. If it does not find one it uses cursor for previous page to get cursor for current page. Cursor is updated in memcache every time user views page. So it should stay up to date.

Having pager object you can render HTML with Jinja2 like this:

{% if pager %}
<ul class="pager">
  <li class="previous{% if not pager.has_prev %} disabled{% endif %}">
    <a href="{{ url_for_other_page(pager.prev_page) }}">Previous</a>
  </li>
  <li class="next{% if not pager.has_next %} disabled{% endif %}">
    <a href="{{ url_for_other_page(pager.next_page) }}">Next</a>
  </li>
</ul>
{% endif %}