Обойти страницы результата#
Стратегия пагинации принадлежит классу операции. Обычный вызов возвращает одну страницу, а
pages() и items() строят следующие запросы через неизменяемое значение операции.
# examples/docs/pagination.py
@dataclass(frozen=True, slots=True, kw_only=True)
class ListDocuments(HttpOperation[DocumentPage]):
__http__ = Http.get("/documents")
__pages__ = Pages.numbered(
DocumentPage,
page="page",
size="per_page",
items=lambda result: result.items,
total_pages=lambda result: result.pages_count,
)
page: Query[int] = 1
per_page: Query[int] = 2
class DocumentsApi(SyncApi):
list = op(ListDocuments)
def main() -> None:
with guide_client(GuideSite()) as client:
documents = DocumentsApi(client)
for number, page in enumerate(documents.list.pages(), start=1):
print(f"page {number}: {', '.join(page.items)}")
page= и size= называют поля Python-класса, а не параметры на проводе. items= извлекает
элементы, total_pages= сообщает последнюю страницу. Несовпадающий тип результата или опечатка
в имени поля останавливает объявление роутера при импорте.
Файл целиком
"""Traverse numbered pages declared on an operation."""
from dataclasses import dataclass
from pydantic import BaseModel
from eazy_sdk import Http, HttpOperation, Query, SyncApi, op
from eazy_sdk.pagination import Pages
from examples.docs.guide_site import GuideSite, guide_client
class DocumentPage(BaseModel):
items: list[str]
pages_count: int
# region docs: pagination
# examples/docs/pagination.py
@dataclass(frozen=True, slots=True, kw_only=True)
class ListDocuments(HttpOperation[DocumentPage]):
__http__ = Http.get("/documents")
__pages__ = Pages.numbered(
DocumentPage,
page="page",
size="per_page",
items=lambda result: result.items,
total_pages=lambda result: result.pages_count,
)
page: Query[int] = 1
per_page: Query[int] = 2
class DocumentsApi(SyncApi):
list = op(ListDocuments)
def main() -> None:
with guide_client(GuideSite()) as client:
documents = DocumentsApi(client)
for number, page in enumerate(documents.list.pages(), start=1):
print(f"page {number}: {', '.join(page.items)}")
# endregion docs: pagination
if __name__ == "__main__":
main()
page 1: Иск, Отзыв
page 2: Решение
Выбрать стратегию#
Стратегия |
Поле следующего запроса |
Признак окончания |
|---|---|---|
|
Номер страницы |
Общее число страниц, короткая или пустая страница |
|
Смещение |
Общее число элементов, короткая или пустая страница |
|
Cursor из ответа |
Отсутствующий следующий cursor |
|
Готовая ссылка |
Отсутствующая ссылка |
items() разворачивает страницы в один поток. Аргумент key= пропускает повторяющиеся элементы
и завершает обход, если новая страница не добавила ни одного значения. max_pages= ограничивает
число запросов, а options= применяется к каждой странице.
max_items= ограничивает число элементов: items(max_items=5) отдаёт первые пять и не
запрашивает страницы после той, на которой нашёлся пятый. Повторы, пропущенные по key=, в счёт
не идут.
first() возвращает первый элемент или None, если первая страница пуста, и всегда делает
ровно один запрос: latest = await api.documents.first(case_id=cid).
Тип элемента берётся из items= стратегии: если items=lambda r: r.items возвращает
list[Document], то items() отдаёт Document, first() возвращает Document | None, а
key= принимает Document. Отдельная аннотация в вызывающем коде не нужна.
Каждая страница проходит обычные retry, middleware, авторизацию, защиту и подпись. Синхронный роутер возвращает iterator, асинхронный async iterator.