Skip to content

Commit 7b6d4fa

Browse files
feat: Setup pagination dependency injections
1 parent afa198b commit 7b6d4fa

4 files changed

Lines changed: 39 additions & 16 deletions

File tree

apps/backend/src/api/deps/pagination.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from fastapi import Query, HTTPException
22
from math import ceil
3-
from typing import Optional
3+
from typing import List, Optional, Type, Dict
4+
5+
from pydantic import BaseModel
46
from ...core.constants import Constants
57

68

@@ -24,8 +26,8 @@ def paginate_query(query, pagination: dict):
2426
page = pagination.get('page', 1)
2527
per_page = pagination.get('per_page', Constants.DEFAULT_PAGE_SIZE)
2628

27-
if per_page > Constants.MAX_PER_PAGE:
28-
raise HTTPException(status_code=400, detail=f'per_page cannot exceed {Constants.MAX_PER_PAGE}')
29+
if per_page > Constants.MAX_PAGE_SIZE:
30+
raise HTTPException(status_code=400, detail=f'per_page cannot exceed {Constants.MAX_PAGE_SIZE}')
2931

3032
offset = (page - 1) * per_page
3133
return query.limit(per_page).offset(offset)
@@ -48,3 +50,10 @@ def build_paginated_response(items: list, total: int, page: int, per_page: int):
4850
},
4951
'results': items,
5052
}
53+
54+
def get_paginated_response_model(item_model: Type[BaseModel]) -> Type[BaseModel]:
55+
class PaginatedResponse(BaseModel):
56+
meta: Dict[str, int]
57+
results: List[item_model]
58+
59+
return PaginatedResponse

apps/backend/src/api/v1/routes/jobs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
# raise HTTPException(status_code=404, detail='Job not found')
2121
# return db_job
2222

23-
@router.get('/', response_model=list[schemas.JobBase])
23+
24+
@router.get('/', response_model=job_service.PaginatedJobs)
2425
def list_jobs(query: str | None = None, pagination: dict = Depends(pagination_params), db: Session = Depends(get_db)):
2526
return job_service.get_jobs(db, pagination, query)
2627

apps/backend/src/models/job.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,12 @@ class Job(Base):
2626

2727
years_of_experience: Mapped[dict] = mapped_column(JSON, nullable=True)
2828

29-
company: Mapped['Company'] = relationship('Company', back_populates='jobs')
29+
company: Mapped['Company'] = relationship('Company')
3030
company_id: Mapped[int] = mapped_column(ForeignKey('companies.id'), nullable=True)
3131

3232
required_skills: Mapped[list['Skill']] = relationship(
3333
'Skill',
34-
secondary=job_skill_table,
35-
back_populates='jobs',
34+
secondary=job_skill_table
3635
)
3736

3837
def __repr__(self):

apps/backend/src/services/job.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,32 @@
1-
import sqlalchemy
1+
from typing import Type
22
from sqlalchemy.orm import Session
33
from ..models.job import Job
4-
from ..schemas.job import JobBase
5-
from ..api.deps.pagination import paginate_query
4+
from ..schemas.job import JobBase, JobRead
5+
from ..api.deps.pagination import build_paginated_response, get_paginated_response_model, paginate_query
66

77
# allowed_job_query_fields = [JobBase.title, JobBase.description, JobBase.company]
88

9-
def get_jobs(db: Session, pagination: dict, query: str | None = None) -> list[JobBase]:
9+
PaginatedJobs = get_paginated_response_model(JobBase)
10+
11+
def get_jobs(db: Session, pagination: dict, query: str | None = None) -> PaginatedJobs:
1012
# query maybe like this query="company: Acme Inc,location: Remote"
1113
q = db.query(Job)
1214

15+
# Optional search filter
1316
# if query:
14-
# query_filters = []
15-
# for field in allowed_job_query_fields:
16-
# query_filters.append(getattr(JobBase, field).ilike(f'%{query}%'))
17-
# q = q.filter(sqlalchemy.or_(*query_filters))
18-
return paginate_query(q, pagination)
17+
# filters = [getattr(Job, field).ilike(f"%{query}%") for field in allowed_job_query_fields]
18+
# q = q.filter(or_(*filters))
19+
20+
total = q.count() # total before pagination
21+
q = paginate_query(q, pagination)
22+
23+
results = q.all()
24+
25+
# convert SQLAlchemy models to Pydantic
26+
jobs = [JobRead.from_orm(job) for job in results]
27+
28+
return build_paginated_response(
29+
items=jobs,
30+
total=total,
31+
**pagination
32+
)

0 commit comments

Comments
 (0)