feat: projects create, update, delete

This commit is contained in:
2025-08-13 15:01:22 +04:00
parent 71c0901909
commit 5e20da8356
4 changed files with 156 additions and 7 deletions

View File

@ -1,7 +1,12 @@
from datetime import datetime
from typing import Optional
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.orm import selectinload
from models.project import Project from models.project import Project
from repositories.base import BaseRepository from repositories.base import BaseRepository
from schemas.project import CreateProjectSchema, UpdateProjectSchema
class ProjectRepository(BaseRepository): class ProjectRepository(BaseRepository):
@ -9,3 +14,39 @@ class ProjectRepository(BaseRepository):
stmt = select(Project).where(Project.is_deleted.is_(False)) stmt = select(Project).where(Project.is_deleted.is_(False))
result = await self.session.execute(stmt) result = await self.session.execute(stmt)
return list(result.scalars().all()) return list(result.scalars().all())
async def get_by_id(self, project_id: int) -> Optional[Project]:
stmt = (
select(Project)
.where(Project.id == project_id, Project.is_deleted.is_(False))
.options(selectinload(Project.boards))
)
result = await self.session.execute(stmt)
return result.scalar_one_or_none()
async def create(self, data: CreateProjectSchema) -> Project:
project_data = data.model_dump()
project_data["created_at"] = datetime.now()
project = Project(**project_data)
self.session.add(project)
await self.session.commit()
await self.session.refresh(project)
return project
async def update(self, project: Project, data: UpdateProjectSchema) -> Project:
project.name = data.name if data.name else project.name
self.session.add(project)
await self.session.commit()
await self.session.refresh(project)
return project
async def delete(self, project: Project, is_soft: bool):
if not is_soft:
await self.session.delete(project)
await self.session.commit()
return
project.is_deleted = True
self.session.add(project)
await self.session.commit()

View File

@ -1,7 +1,14 @@
from fastapi import APIRouter from fastapi import APIRouter, Query
from backend.dependecies import SessionDependency from backend.dependecies import SessionDependency
from schemas.project import GetProjectsResponse from schemas.project import (
GetProjectsResponse,
CreateProjectResponse,
CreateProjectRequest,
UpdateProjectResponse,
UpdateProjectRequest,
DeleteProjectResponse,
)
from services import ProjectService from services import ProjectService
project_router = APIRouter( project_router = APIRouter(
@ -18,3 +25,40 @@ async def get_projects(
session: SessionDependency, session: SessionDependency,
): ):
return await ProjectService(session).get_projects() return await ProjectService(session).get_projects()
@project_router.post(
"/",
response_model=CreateProjectResponse,
operation_id="create_project",
)
async def create_project(
session: SessionDependency,
request: CreateProjectRequest,
):
return await ProjectService(session).create_project(request)
@project_router.patch(
"/{projectId}",
response_model=UpdateProjectResponse,
operation_id="update_project",
)
async def update_project(
session: SessionDependency,
request: UpdateProjectRequest,
project_id: int = Query(alias="projectId"),
):
return await ProjectService(session).update_project(project_id, request)
@project_router.delete(
"/{projectId}",
response_model=DeleteProjectResponse,
operation_id="delete_project",
)
async def delete_project(
session: SessionDependency,
project_id: int = Query(alias="projectId"),
):
return await ProjectService(session).delete_project(project_id)

View File

@ -1,15 +1,22 @@
from schemas.base import BaseSchema from typing import Optional
from schemas.base import BaseSchema, BaseResponse
# region Entity # region Entity
class BaseProjectSchema(BaseSchema): class ProjectSchema(BaseSchema):
id: int
name: str name: str
class ProjectSchema(BaseProjectSchema): class CreateProjectSchema(BaseSchema):
id: int name: str
class UpdateProjectSchema(BaseSchema):
name: Optional[str] = None
# endregion # endregion
@ -17,6 +24,14 @@ class ProjectSchema(BaseProjectSchema):
# region Requests # region Requests
class CreateProjectRequest(BaseSchema):
project: CreateProjectSchema
class UpdateProjectRequest(BaseSchema):
project: UpdateProjectSchema
# endregion # endregion
# region Responses # region Responses
@ -26,4 +41,16 @@ class GetProjectsResponse(BaseSchema):
projects: list[ProjectSchema] projects: list[ProjectSchema]
class CreateProjectResponse(BaseResponse):
project: ProjectSchema
class UpdateProjectResponse(BaseResponse):
pass
class DeleteProjectResponse(BaseResponse):
pass
# endregion Responses # endregion Responses

View File

@ -1,7 +1,16 @@
from fastapi import HTTPException
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from repositories import ProjectRepository from repositories import ProjectRepository
from schemas.project import GetProjectsResponse, ProjectSchema from schemas.project import (
GetProjectsResponse,
ProjectSchema,
CreateProjectRequest,
CreateProjectResponse,
UpdateProjectRequest,
UpdateProjectResponse,
DeleteProjectResponse,
)
class ProjectService: class ProjectService:
@ -13,3 +22,31 @@ class ProjectService:
return GetProjectsResponse( return GetProjectsResponse(
projects=[ProjectSchema.model_validate(project) for project in projects] projects=[ProjectSchema.model_validate(project) for project in projects]
) )
async def create_project(
self, request: CreateProjectRequest
) -> CreateProjectResponse:
project = await self.repository.create(request.project)
return CreateProjectResponse(
project=ProjectSchema.model_validate(project),
message="Проект успешно создан",
)
async def update_project(
self, project_id: int, request: UpdateProjectRequest
) -> UpdateProjectResponse:
project = await self.repository.get_by_id(project_id)
if not project:
raise HTTPException(status_code=404, detail="Проект не найден")
await self.repository.update(project, request.project)
return UpdateProjectResponse(message="Проект успешно обновлен")
async def delete_project(self, project_id: int) -> DeleteProjectResponse:
project = await self.repository.get_by_id(project_id)
if not project:
raise HTTPException(status_code=404, detail="Проект не найден")
is_soft_needed: bool = len(project.boards) > 0
await self.repository.delete(project, is_soft_needed)
return DeleteProjectResponse(message="Проект успешно удален")