Compare commits

...

2 Commits

Author SHA1 Message Date
IluaAir
7e9346ec4d add base update one 2025-09-06 13:53:18 +03:00
IluaAir
d7e522d362 add patch endpoint and service update_task 2025-09-03 23:55:26 +03:00
7 changed files with 55 additions and 21 deletions

View File

@@ -1,11 +1,11 @@
from typing import Annotated from typing import Annotated
from fastapi import APIRouter, Depends from fastapi import APIRouter, Body, Depends
from src.api.dependacies.db_dep import sessionDep from src.api.dependacies.db_dep import sessionDep
from src.api.dependacies.task_dep import TaskFilterDep from src.api.dependacies.task_dep import TaskFilterDep
from src.api.dependacies.user_dep import ActiveUser, TaskOwnerDep from src.api.dependacies.user_dep import ActiveUser, TaskOwnerDep
from src.schemas.tasks import TaskADDRequest from src.schemas.tasks import TaskADDRequest, TaskPATCHRequest
from src.services.tasks import TaskService from src.services.tasks import TaskService
from src.services.users import UserService from src.services.users import UserService
@@ -13,14 +13,9 @@ router = APIRouter(prefix="/tasks", tags=["Tasks"])
@router.get("/") @router.get("/")
async def get_tasks( async def get_tasks(session: sessionDep, user: ActiveUser, filter: TaskFilterDep):
session: sessionDep,
user: ActiveUser,
filter: TaskFilterDep
):
result = await UserService(session).get_user_with_tasks( result = await UserService(session).get_user_with_tasks(
user_id=user.id, user_id=user.id, **filter.model_dump(exclude_unset=True)
**filter.model_dump(exclude_unset=True)
) )
return result return result
@@ -43,6 +38,17 @@ async def post_task(
return result return result
@router.patch("/{id}")
async def patch_task(
session: sessionDep,
id: int,
_: TaskOwnerDep,
task_data: TaskPATCHRequest = Body(),
):
task = await TaskService(session).update_task(id, task_data)
return task
@router.delete("/{id}") @router.delete("/{id}")
async def delete_task( async def delete_task(
session: sessionDep, session: sessionDep,

View File

@@ -1,15 +1,20 @@
from typing import Any, Protocol from typing import TYPE_CHECKING, Any, Protocol
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from src.repository.tasks import TasksRepo if TYPE_CHECKING:
from src.repository.users import UsersRepo from src.repository.tasks import TasksRepo
from src.repository.users import UsersRepo
class HasId(Protocol):
id: Any
class IUOWDB(Protocol): class IUOWDB(Protocol):
session: AsyncSession session: AsyncSession
user: UsersRepo user: 'UsersRepo'
task: TasksRepo task: 'TasksRepo'
async def __aenter__(self) -> "IUOWDB": ... async def __aenter__(self) -> "IUOWDB": ...

View File

@@ -1,11 +1,11 @@
from typing import Any, Generic, Mapping, Sequence, Type, TypeVar from typing import Any, Generic, Mapping, Sequence, Type, TypeVar
from sqlalchemy import delete, insert, select from sqlalchemy import delete, insert, select, update
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from src.core.database import Base from src.core.interfaces import HasId
ModelType = TypeVar("ModelType", bound=Base) ModelType = TypeVar("ModelType", bound=HasId)
class BaseRepo(Generic[ModelType]): class BaseRepo(Generic[ModelType]):
@@ -44,3 +44,14 @@ class BaseRepo(Generic[ModelType]):
async def delete_one(self, **filter_by) -> None: async def delete_one(self, **filter_by) -> None:
await self.session.execute(delete(self.model).filter_by(**filter_by)) await self.session.execute(delete(self.model).filter_by(**filter_by))
async def update_one(self, id: int, data: Mapping[str, Any]) -> ModelType:
stmt = (
update(self.model)
.where(self.model.id == id)
.values(data)
.returning(self.model)
)
result = await self.session.execute(stmt)
model = result.scalar_one()
return model

View File

@@ -50,7 +50,11 @@ class UsersRepo(BaseRepo):
selectinload( selectinload(
self.model.tasks.and_(TasksORM.id.in_(tasks_subquery)) self.model.tasks.and_(TasksORM.id.in_(tasks_subquery))
).load_only( ).load_only(
TasksORM.id, TasksORM.title, TasksORM.due_date, TasksORM.priority, TasksORM.status TasksORM.id,
TasksORM.title,
TasksORM.due_date,
TasksORM.priority,
TasksORM.status,
) )
) )
) )

View File

@@ -1,7 +1,7 @@
from fastapi import HTTPException from fastapi import HTTPException
from src.models.tasks import TasksORM from src.models.tasks import TasksORM
from src.schemas.tasks import Task, TaskADDRequest from src.schemas.tasks import Task, TaskADDRequest, TaskPATCHRequest
from src.services.base import BaseService from src.services.base import BaseService
@@ -27,3 +27,12 @@ class TaskService(BaseService):
async def delete_task(self, task_id: int): async def delete_task(self, task_id: int):
await self.session.task.delete_one(id=task_id) await self.session.task.delete_one(id=task_id)
await self.session.commit() await self.session.commit()
async def update_task(
self, task_id: int, task_data: TaskPATCHRequest, exclude_unset: bool = True
):
task = await self.session.task.update_one(
id=task_id, data=task_data.model_dump(exclude_unset=exclude_unset)
)
await self.session.commit()
return Task.model_validate(task)

View File

@@ -49,7 +49,6 @@ class UserService(BaseService):
offset: int | None, offset: int | None,
date_to: date | None, date_to: date | None,
date_from: date | None, date_from: date | None,
): ):
user = await self.session.user.get_one_with_load( user = await self.session.user.get_one_with_load(
user_id=user_id, user_id=user_id,

View File

@@ -1,4 +1,4 @@
import json
import pytest import pytest
from httpx import ASGITransport, AsyncClient from httpx import ASGITransport, AsyncClient
from sqlalchemy import NullPool, insert from sqlalchemy import NullPool, insert