feat: pagination and query params for a deal end-point
This commit is contained in:
@ -1,6 +1,13 @@
|
|||||||
|
from sqlalchemy import Select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
|
||||||
class BaseRepository:
|
class BaseRepository:
|
||||||
def __init__(self, session: AsyncSession):
|
def __init__(self, session: AsyncSession):
|
||||||
self.session = session
|
self.session = session
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _apply_pagination(query: Select, page: int, items_per_page: int) -> Select:
|
||||||
|
offset = (page - 1) * items_per_page
|
||||||
|
query = query.offset(offset).limit(items_per_page)
|
||||||
|
return query
|
||||||
|
|||||||
@ -2,20 +2,35 @@ from typing import Optional
|
|||||||
|
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
|
||||||
from models import Deal, CardStatusHistory
|
from models import Deal, CardStatusHistory, Board
|
||||||
from repositories.base import BaseRepository
|
from repositories.base import BaseRepository
|
||||||
from schemas.deal import UpdateDealSchema, CreateDealSchema
|
from schemas.deal import UpdateDealSchema, CreateDealSchema
|
||||||
|
|
||||||
|
|
||||||
class DealRepository(BaseRepository):
|
class DealRepository(BaseRepository):
|
||||||
async def get_all(self, board_id: int) -> list[Deal]:
|
async def get_all(
|
||||||
stmt = (
|
self,
|
||||||
select(Deal)
|
board_id: Optional[int],
|
||||||
.where(Deal.is_deleted.is_(False), Deal.board_id == board_id)
|
project_id: Optional[int],
|
||||||
.order_by(Deal.lexorank)
|
page: Optional[int],
|
||||||
)
|
items_per_page: Optional[int],
|
||||||
|
) -> tuple[list[Deal], int]:
|
||||||
|
stmt = select(Deal).where(Deal.is_deleted.is_(False))
|
||||||
|
|
||||||
|
if board_id:
|
||||||
|
stmt = stmt.where(Deal.board_id == board_id)
|
||||||
|
if project_id:
|
||||||
|
stmt = stmt.join(Board).where(Board.project_id == project_id)
|
||||||
|
|
||||||
|
total_items = len((await self.session.execute(stmt)).all())
|
||||||
|
|
||||||
|
stmt = stmt.order_by(Deal.lexorank)
|
||||||
|
|
||||||
|
if page and items_per_page:
|
||||||
|
stmt = self._apply_pagination(stmt, page, items_per_page)
|
||||||
|
|
||||||
result = await self.session.execute(stmt)
|
result = await self.session.execute(stmt)
|
||||||
return list(result.scalars().all())
|
return list(result.scalars().all()), total_items
|
||||||
|
|
||||||
async def get_by_id(self, deal_id: int) -> Optional[Deal]:
|
async def get_by_id(self, deal_id: int) -> Optional[Deal]:
|
||||||
stmt = select(Deal).where(Deal.id == deal_id, Deal.is_deleted.is_(False))
|
stmt = select(Deal).where(Deal.id == deal_id, Deal.is_deleted.is_(False))
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
from fastapi import APIRouter, Path
|
from fastapi import APIRouter, Path, Query
|
||||||
|
|
||||||
from backend.dependecies import SessionDependency
|
from backend.dependecies import SessionDependency, PaginationDependency
|
||||||
from schemas.deal import *
|
from schemas.deal import *
|
||||||
from services import DealService
|
from services import DealService
|
||||||
|
|
||||||
@ -10,15 +10,17 @@ deal_router = APIRouter(
|
|||||||
|
|
||||||
|
|
||||||
@deal_router.get(
|
@deal_router.get(
|
||||||
"/{boardId}",
|
"/",
|
||||||
response_model=GetDealsResponse,
|
response_model=GetDealsResponse,
|
||||||
operation_id="get_deals",
|
operation_id="get_deals",
|
||||||
)
|
)
|
||||||
async def get_deals(
|
async def get_deals(
|
||||||
session: SessionDependency,
|
session: SessionDependency,
|
||||||
board_id: int = Path(alias="boardId"),
|
pagination: PaginationDependency,
|
||||||
|
board_id: Optional[int] = Query(alias="boardId", default=None),
|
||||||
|
project_id: Optional[int] = Query(alias="projectId", default=None),
|
||||||
):
|
):
|
||||||
return await DealService(session).get_deals(board_id)
|
return await DealService(session).get_deals(pagination, board_id, project_id)
|
||||||
|
|
||||||
|
|
||||||
@deal_router.post(
|
@deal_router.post(
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from schemas.base import BaseSchema, BaseResponse
|
from schemas.base import BaseSchema, BaseResponse, PaginationInfoSchema
|
||||||
|
|
||||||
|
|
||||||
# region Entities
|
# region Entities
|
||||||
@ -48,6 +48,7 @@ class UpdateDealRequest(BaseSchema):
|
|||||||
|
|
||||||
class GetDealsResponse(BaseSchema):
|
class GetDealsResponse(BaseSchema):
|
||||||
deals: list[DealSchema]
|
deals: list[DealSchema]
|
||||||
|
pagination_info: PaginationInfoSchema
|
||||||
|
|
||||||
|
|
||||||
class CreateDealResponse(BaseResponse):
|
class CreateDealResponse(BaseResponse):
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from repositories import DealRepository
|
from repositories import DealRepository
|
||||||
|
from schemas.base import PaginationSchema
|
||||||
from schemas.deal import *
|
from schemas.deal import *
|
||||||
|
|
||||||
|
|
||||||
@ -9,10 +12,25 @@ class DealService:
|
|||||||
def __init__(self, session: AsyncSession):
|
def __init__(self, session: AsyncSession):
|
||||||
self.repository = DealRepository(session)
|
self.repository = DealRepository(session)
|
||||||
|
|
||||||
async def get_deals(self, board_id: int) -> GetDealsResponse:
|
async def get_deals(
|
||||||
deals = await self.repository.get_all(board_id)
|
self,
|
||||||
|
pagination: PaginationSchema,
|
||||||
|
board_id: Optional[int],
|
||||||
|
project_id: Optional[int],
|
||||||
|
) -> GetDealsResponse:
|
||||||
|
deals, total_items = await self.repository.get_all(
|
||||||
|
board_id, project_id, pagination.page, pagination.items_per_page
|
||||||
|
)
|
||||||
|
|
||||||
|
total_pages = 1
|
||||||
|
if pagination.items_per_page:
|
||||||
|
total_pages = math.ceil(total_items / pagination.items_per_page)
|
||||||
|
|
||||||
return GetDealsResponse(
|
return GetDealsResponse(
|
||||||
deals=[DealSchema.model_validate(deal) for deal in deals]
|
deals=[DealSchema.model_validate(deal) for deal in deals],
|
||||||
|
pagination_info=PaginationInfoSchema(
|
||||||
|
total_pages=total_pages, total_items=total_items
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
async def create_deal(self, request: CreateDealRequest) -> CreateDealResponse:
|
async def create_deal(self, request: CreateDealRequest) -> CreateDealResponse:
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastapi import Query
|
||||||
|
|
||||||
from schemas.base import PaginationSchema
|
from schemas.base import PaginationSchema
|
||||||
|
|
||||||
|
|
||||||
async def pagination_parameters(
|
async def pagination_parameters(
|
||||||
page: Optional[int] = None, items_per_page: Optional[int] = None
|
page: Optional[int] = Query(default=None),
|
||||||
|
items_per_page: Optional[int] = Query(default=None, alias="itemsPerPage"),
|
||||||
) -> PaginationSchema:
|
) -> PaginationSchema:
|
||||||
return PaginationSchema(page=page, items_per_page=items_per_page)
|
return PaginationSchema(page=page, items_per_page=items_per_page)
|
||||||
|
|
||||||
@ -12,6 +15,6 @@ async def pagination_parameters(
|
|||||||
def is_valid_pagination(pagination: Optional[PaginationSchema]) -> bool:
|
def is_valid_pagination(pagination: Optional[PaginationSchema]) -> bool:
|
||||||
if not pagination:
|
if not pagination:
|
||||||
return False
|
return False
|
||||||
return all(
|
return isinstance(pagination.items_per_page, int) and isinstance(
|
||||||
[isinstance(pagination.items_per_page, int), isinstance(pagination.page, int)]
|
pagination.page, int
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user