Fix the Repository ID in pagination problem once and for all

But.... ONCE AND FOR ALL!

Note: Tested on SQLite, Postgres and MySQL
This commit is contained in:
Joseph Schorr 2016-07-14 17:09:52 -04:00
parent 1ed1bc9ed3
commit 4e1259b58a
2 changed files with 16 additions and 10 deletions

View file

@ -1,29 +1,34 @@
def paginate(query, model, descending=False, page_token=None, limit=50, id_field='id'): from peewee import SQL
def paginate(query, model, descending=False, page_token=None, limit=50, id_alias=None):
""" Paginates the given query using an ID range, starting at the optional page_token. """ Paginates the given query using an ID range, starting at the optional page_token.
Returns a *list* of matching results along with an unencrypted page_token for the Returns a *list* of matching results along with an unencrypted page_token for the
next page, if any. If descending is set to True, orders by the ID descending rather next page, if any. If descending is set to True, orders by the ID descending rather
than ascending. than ascending.
""" """
query = query.limit(limit + 1) id_field = model.id
if id_alias is not None:
id_field = SQL(id_alias)
if descending: if descending:
query = query.order_by(getattr(model, id_field).desc()) query = query.order_by(id_field.desc())
else: else:
query = query.order_by(getattr(model, id_field)) query = query.order_by(id_field)
if page_token is not None: if page_token is not None:
start_id = page_token.get('start_id') start_id = page_token.get('start_id')
if start_id is not None: if start_id is not None:
if descending: if descending:
query = query.where(getattr(model, id_field) <= start_id) query = query.where(model.id <= start_id)
else: else:
query = query.where(getattr(model, id_field) >= start_id) query = query.where(model.id >= start_id)
results = list(query) results = list(query)
page_token = None page_token = None
if len(results) > limit: if len(results) > limit:
start_id = results[limit].id
page_token = { page_token = {
'start_id': getattr(results[limit], id_field) 'start_id': start_id
} }
return results[0:limit], page_token return results[0:limit], page_token

View file

@ -9,10 +9,11 @@ from datetime import timedelta, datetime
from flask import request, abort from flask import request, abort
from data import model from data import model
from data.database import Repository as RepositoryTable
from endpoints.api import (truthy_bool, format_date, nickname, log_action, validate_json_request, from endpoints.api import (truthy_bool, format_date, nickname, log_action, validate_json_request,
require_repo_read, require_repo_write, require_repo_admin, require_repo_read, require_repo_write, require_repo_admin,
RepositoryParamResource, resource, query_param, parse_args, ApiResource, RepositoryParamResource, resource, query_param, parse_args, ApiResource,
request_error, require_scope, path_param, page_support, parse_args, request_error, require_scope, path_param, page_support, parse_args,
query_param, truthy_bool) query_param, truthy_bool)
from endpoints.exception import Unauthorized, NotFound, InvalidRequest, ExceedsLicenseException from endpoints.exception import Unauthorized, NotFound, InvalidRequest, ExceedsLicenseException
from endpoints.api.billing import lookup_allowed_private_repos, get_namespace_plan from endpoints.api.billing import lookup_allowed_private_repos, get_namespace_plan
@ -165,9 +166,9 @@ class RepositoryList(ApiResource):
# Note: We only limit repositories when there isn't a namespace or starred filter, as they # Note: We only limit repositories when there isn't a namespace or starred filter, as they
# result in far smaller queries. # result in far smaller queries.
if not parsed_args['namespace'] and not parsed_args['starred']: if not parsed_args['namespace'] and not parsed_args['starred']:
repos, next_page_token = model.modelutil.paginate(repo_query, repo_query.c, repos, next_page_token = model.modelutil.paginate(repo_query, RepositoryTable,
page_token=page_token, limit=REPOS_PER_PAGE, page_token=page_token, limit=REPOS_PER_PAGE,
id_field='rid') id_alias='rid')
else: else:
repos = list(repo_query) repos = list(repo_query)
next_page_token = None next_page_token = None