Files
syndarix/backend/app/schemas/common.py
Felipe Cardoso c684f2ba95 Add UUID handling, sorting, filtering, and soft delete functionality to CRUD operations
- Enhanced UUID validation by supporting both string and `UUID` formats.
- Added advanced filtering and sorting capabilities to `get_multi_with_total` method.
- Introduced soft delete and restore functionality for models with `deleted_at` column.
- Updated tests to reflect new endpoints and rate-limiting logic.
- Improved schema definitions with `SortParams` and `SortOrder` for consistent API inputs.
2025-10-30 16:44:15 +01:00

169 lines
4.1 KiB
Python

"""
Common schemas used across the API for pagination, responses, filtering, and sorting.
"""
from typing import Generic, TypeVar, List, Optional
from enum import Enum
from pydantic import BaseModel, Field
from math import ceil
T = TypeVar('T')
class SortOrder(str, Enum):
"""Sort order options."""
ASC = "asc"
DESC = "desc"
class PaginationParams(BaseModel):
"""Parameters for pagination."""
page: int = Field(
default=1,
ge=1,
description="Page number (1-indexed)"
)
limit: int = Field(
default=20,
ge=1,
le=100,
description="Number of items per page (max 100)"
)
@property
def offset(self) -> int:
"""Calculate the offset for database queries."""
return (self.page - 1) * self.limit
@property
def skip(self) -> int:
"""Alias for offset (compatibility with existing code)."""
return self.offset
model_config = {
"json_schema_extra": {
"example": {
"page": 1,
"limit": 20
}
}
}
class SortParams(BaseModel):
"""Parameters for sorting."""
sort_by: Optional[str] = Field(
default=None,
description="Field name to sort by"
)
sort_order: SortOrder = Field(
default=SortOrder.ASC,
description="Sort order (asc or desc)"
)
model_config = {
"json_schema_extra": {
"example": {
"sort_by": "created_at",
"sort_order": "desc"
}
}
}
class PaginationMeta(BaseModel):
"""Metadata for paginated responses."""
total: int = Field(..., description="Total number of items")
page: int = Field(..., description="Current page number")
page_size: int = Field(..., description="Number of items in current page")
total_pages: int = Field(..., description="Total number of pages")
has_next: bool = Field(..., description="Whether there is a next page")
has_prev: bool = Field(..., description="Whether there is a previous page")
model_config = {
"json_schema_extra": {
"example": {
"total": 150,
"page": 1,
"page_size": 20,
"total_pages": 8,
"has_next": True,
"has_prev": False
}
}
}
class PaginatedResponse(BaseModel, Generic[T]):
"""Generic paginated response wrapper."""
data: List[T] = Field(..., description="List of items")
pagination: PaginationMeta = Field(..., description="Pagination metadata")
model_config = {
"json_schema_extra": {
"example": {
"data": [
{"id": "123", "name": "Example Item"}
],
"pagination": {
"total": 150,
"page": 1,
"page_size": 20,
"total_pages": 8,
"has_next": True,
"has_prev": False
}
}
}
}
class MessageResponse(BaseModel):
"""Simple message response."""
success: bool = Field(default=True, description="Operation success status")
message: str = Field(..., description="Human-readable message")
model_config = {
"json_schema_extra": {
"example": {
"success": True,
"message": "Operation completed successfully"
}
}
}
def create_pagination_meta(
total: int,
page: int,
limit: int,
items_count: int
) -> PaginationMeta:
"""
Helper function to create pagination metadata.
Args:
total: Total number of items
page: Current page number
limit: Items per page
items_count: Number of items in current page
Returns:
PaginationMeta object with calculated values
"""
total_pages = ceil(total / limit) if limit > 0 else 0
return PaginationMeta(
total=total,
page=page,
page_size=items_count,
total_pages=total_pages,
has_next=page < total_pages,
has_prev=page > 1
)