feat(backend): add FastAPI backend — procurement, inventory domain, Casdoor auth, PM arrival endpoints
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
"""Pagination + sorting helpers."""
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import InstrumentedAttribute
|
||||
|
||||
from app.config import settings
|
||||
from app.utils.errors import ValidationError
|
||||
|
||||
|
||||
async def paginate(query, page: int, page_size: int, db: AsyncSession):
|
||||
"""Apply offset/limit to `query` and return (items, meta dict).
|
||||
|
||||
`page_size` is clamped to [1, MAX_PAGE_SIZE] after validation — routers
|
||||
enforce the contract bounds via Query(ge=1, le=100) before calling this.
|
||||
"""
|
||||
total = await db.scalar(select(func.count()).select_from(query.subquery()))
|
||||
items = (await db.execute(query.offset((page - 1) * page_size).limit(page_size))).scalars().all()
|
||||
return items, {"total": total or 0, "page": page, "page_size": page_size}
|
||||
|
||||
|
||||
def sort_query(stmt, model, sort: str | None, default: str = "-created_at"):
|
||||
"""Apply `?sort=-created_at` (leading `-` = descending) against a model.
|
||||
|
||||
Only real columns of the model are accepted — anything else is a
|
||||
VALIDATION_ERROR (400), never a raw SQL fragment.
|
||||
"""
|
||||
raw = (sort or default).strip()
|
||||
descending = raw.startswith("-")
|
||||
name = raw[1:] if descending else raw
|
||||
column: InstrumentedAttribute | None = getattr(model, name, None)
|
||||
if column is None or not isinstance(column, InstrumentedAttribute):
|
||||
raise ValidationError(f"فیلد مرتبسازی نامعتبر است: {name}", field="sort")
|
||||
return stmt.order_by(column.desc() if descending else column.asc())
|
||||
|
||||
|
||||
def default_pagination(page: int | None, page_size: int | None) -> tuple[int, int]:
|
||||
"""Contract defaults: page=1, page_size=20 (max 100) with 400 on out-of-bounds."""
|
||||
p = page if page is not None else 1
|
||||
ps = page_size if page_size is not None else settings.DEFAULT_PAGE_SIZE
|
||||
if p < 1:
|
||||
raise ValidationError("شماره صفحه باید بزرگتر از صفر باشد", field="page")
|
||||
if ps < 1:
|
||||
raise ValidationError("اندازه صفحه باید بزرگتر از صفر باشد", field="page_size")
|
||||
if ps > settings.MAX_PAGE_SIZE:
|
||||
raise ValidationError(f"اندازه صفحه نمیتواند از {settings.MAX_PAGE_SIZE} بیشتر باشد", field="page_size")
|
||||
return p, ps
|
||||
Reference in New Issue
Block a user