feat: attr options and selects editing

This commit is contained in:
2025-11-04 12:18:53 +04:00
parent a7bda3d9f6
commit c266814c96
11 changed files with 239 additions and 40 deletions

View File

@ -13,7 +13,7 @@ if TYPE_CHECKING:
class AttributeSelect(BaseModel, IdMixin, SoftDeleteMixin):
__tablename__ = "attribute_selects"
label: Mapped[str] = mapped_column()
name: Mapped[str] = mapped_column()
is_built_in: Mapped[bool] = mapped_column(
default=False,
comment="Если встроенный select, то запрещено редактировать пользователю",
@ -33,7 +33,7 @@ class AttributeSelect(BaseModel, IdMixin, SoftDeleteMixin):
class AttributeOption(BaseModel, IdMixin, SoftDeleteMixin):
__tablename__ = "attribute_options"
label: Mapped[str] = mapped_column()
name: Mapped[str] = mapped_column()
select_id: Mapped[int] = mapped_column(ForeignKey("attribute_selects.id"))
select: Mapped[AttributeSelect] = relationship(

View File

@ -7,3 +7,4 @@ from .deal_tag import DealTagRepository as DealTagRepository
from .module import ModuleRepository as ModuleRepository
from .project import ProjectRepository as ProjectRepository
from .status import StatusRepository as StatusRepository
from .attr_option import AttrOptionRepository as AttrOptionRepository

View File

@ -0,0 +1,21 @@
from sqlalchemy import Select
from sqlalchemy.ext.asyncio import AsyncSession
from models import AttributeOption
from repositories.mixins import RepCrudMixin
from schemas.attr_option import CreateAttrOptionSchema, UpdateAttrOptionSchema
class AttrOptionRepository(
RepCrudMixin[AttributeOption, CreateAttrOptionSchema, UpdateAttrOptionSchema],
):
session: AsyncSession
entity_class = AttributeOption
entity_not_found_msg = "Опция не найдена"
def _process_get_all_stmt_with_args(self, stmt: Select, *args) -> Select:
select_id = args[0]
return stmt.where(
AttributeOption.select_id == select_id,
AttributeOption.is_deleted.is_(False),
).order_by(AttributeOption.id)

View File

@ -1,19 +1,12 @@
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from models import AttributeSelect, AttributeOption
from repositories.base import BaseRepository
from repositories.mixins import RepGetAllMixin
from models import AttributeSelect
from repositories.mixins import RepCrudMixin
from schemas.attr_select import UpdateAttrSelectSchema, CreateAttrSelectSchema
class AttrSelectRepository(BaseRepository, RepGetAllMixin[AttributeSelect]):
class AttrSelectRepository(
RepCrudMixin[AttributeSelect, CreateAttrSelectSchema, UpdateAttrSelectSchema],
):
session: AsyncSession
entity_class = AttributeSelect
async def get_options(self, select_id: int) -> list[AttributeOption]:
stmt = select(AttributeOption).where(
AttributeOption.select_id == select_id,
AttributeOption.is_deleted.is_(False),
)
result = await self.session.execute(stmt)
return list(result.scalars().all())

View File

@ -0,0 +1,56 @@
from fastapi import APIRouter, Path
from backend.dependecies import SessionDependency
from schemas.attr_option import *
from services import AttrOptionService
router = APIRouter(tags=["attr_option"])
@router.get(
"/{selectId}",
response_model=GetAllAttrSelectOptionsResponse,
operation_id="get_attr_options",
)
async def get_attr_options(
session: SessionDependency,
select_id: int = Path(alias="selectId"),
):
return await AttrOptionService(session).get_all(select_id)
@router.post(
"/",
response_model=CreateAttrOptionResponse,
operation_id="create_attr_option",
)
async def create_attr_select(
session: SessionDependency,
request: CreateAttrOptionRequest,
):
return await AttrOptionService(session).create(request)
@router.patch(
"/{pk}",
response_model=UpdateAttrOptionResponse,
operation_id="update_attr_option",
)
async def update_attr_option(
session: SessionDependency,
request: UpdateAttrOptionRequest,
pk: int = Path(),
):
return await AttrOptionService(session).update(pk, request)
@router.delete(
"/{pk}",
response_model=DeleteAttrOptionResponse,
operation_id="delete_attr_option",
)
async def delete_attr_option(
session: SessionDependency,
pk: int = Path(),
):
return await AttrOptionService(session).delete(pk)

View File

@ -18,13 +18,38 @@ async def get_attr_selects(
return await AttrSelectService(session).get_all()
@router.get(
"/{selectId}",
response_model=GetAllAttrSelectOptionsResponse,
operation_id="get_attr_select_options",
@router.post(
"/",
response_model=CreateAttrSelectResponse,
operation_id="create_attr_select",
)
async def get_attr_select_options(
async def create_attr_select(
session: SessionDependency,
select_id: int = Path(alias="selectId"),
request: CreateAttrSelectRequest,
):
return await AttrSelectService(session).get_options(select_id)
return await AttrSelectService(session).create(request)
@router.patch(
"/{pk}",
response_model=UpdateAttrSelectResponse,
operation_id="update_attr_select",
)
async def update_attr_select(
session: SessionDependency,
request: UpdateAttrSelectRequest,
pk: int = Path(),
):
return await AttrSelectService(session).update(pk, request)
@router.delete(
"/{pk}",
response_model=DeleteAttrSelectResponse,
operation_id="delete_attr_select",
)
async def delete_attr_select(
session: SessionDependency,
pk: int = Path(),
):
return await AttrSelectService(session).delete(pk)

55
schemas/attr_option.py Normal file
View File

@ -0,0 +1,55 @@
from schemas.base import BaseSchema, BaseResponse
# region Entity
class AttrOptionSchema(BaseSchema):
id: int
name: str
class CreateAttrOptionSchema(BaseSchema):
name: str
select_id: int
class UpdateAttrOptionSchema(BaseSchema):
name: str
# endregion
# region Request
class CreateAttrOptionRequest(BaseSchema):
entity: CreateAttrOptionSchema
class UpdateAttrOptionRequest(BaseSchema):
entity: UpdateAttrOptionSchema
# endregion
# region Response
class GetAllAttrSelectOptionsResponse(BaseSchema):
items: list[AttrOptionSchema]
class CreateAttrOptionResponse(BaseSchema):
entity: AttrOptionSchema
class UpdateAttrOptionResponse(BaseResponse):
pass
class DeleteAttrOptionResponse(BaseSchema):
pass
# endregion

View File

@ -1,4 +1,5 @@
from schemas.base import BaseSchema
from schemas.attr_option import AttrOptionSchema
from schemas.base import BaseSchema, BaseResponse
# region Entity
@ -6,13 +7,16 @@ from schemas.base import BaseSchema
class AttrSelectSchema(BaseSchema):
id: int
label: str
name: str
is_built_in: bool
class AttrOptionSchema(BaseSchema):
id: int
label: str
class CreateAttrSelectSchema(BaseSchema):
name: str
class UpdateAttrSelectSchema(BaseSchema):
name: str
class AttrSelectWithOptionsSchema(AttrSelectSchema):
@ -24,6 +28,14 @@ class AttrSelectWithOptionsSchema(AttrSelectSchema):
# region Request
class CreateAttrSelectRequest(BaseSchema):
entity: CreateAttrSelectSchema
class UpdateAttrSelectRequest(BaseSchema):
entity: UpdateAttrSelectSchema
# endregion
# region Response
@ -33,8 +45,16 @@ class GetAllAttrSelectsResponse(BaseSchema):
items: list[AttrSelectSchema]
class GetAllAttrSelectOptionsResponse(BaseSchema):
items: list[AttrOptionSchema]
class CreateAttrSelectResponse(BaseResponse):
entity: AttrSelectSchema
class UpdateAttrSelectResponse(BaseResponse):
pass
class DeleteAttrSelectResponse(BaseResponse):
pass
# endregion

View File

@ -6,3 +6,4 @@ from .deal_group import DealGroupService as DealGroupService
from .deal_tag import DealTagService as DealTagService
from .project import ProjectService as ProjectService
from .status import StatusService as StatusService
from .attr_option import AttrOptionService as AttrOptionService

27
services/attr_option.py Normal file
View File

@ -0,0 +1,27 @@
from sqlalchemy.ext.asyncio import AsyncSession
from models import AttributeOption
from repositories import AttrOptionRepository
from schemas.attr_option import (
AttrOptionSchema,
CreateAttrOptionRequest,
UpdateAttrOptionRequest,
)
from services.mixins import ServiceCrudMixin
class AttrOptionService(
ServiceCrudMixin[
AttributeOption,
AttrOptionSchema,
CreateAttrOptionRequest,
UpdateAttrOptionRequest,
]
):
schema_class = AttrOptionSchema
entity_deleted_msg = "Опция успешно удалена"
entity_updated_msg = "Опция успешно обновлена"
entity_created_msg = "Опция успешно создана"
def __init__(self, session: AsyncSession):
self.repository = AttrOptionRepository(session)

View File

@ -4,21 +4,21 @@ from models import AttributeSelect
from repositories import AttrSelectRepository
from schemas.attr_select import (
AttrSelectSchema,
GetAllAttrSelectOptionsResponse,
AttrOptionSchema,
CreateAttrSelectRequest,
UpdateAttrSelectRequest,
)
from services.mixins import ServiceGetAllMixin
from services.mixins import ServiceCrudMixin
class AttrSelectService(ServiceGetAllMixin[AttributeSelect, AttrSelectSchema]):
class AttrSelectService(
ServiceCrudMixin[
AttributeSelect,
AttrSelectSchema,
CreateAttrSelectRequest,
UpdateAttrSelectRequest,
]
):
schema_class = AttrSelectSchema
def __init__(self, session: AsyncSession):
self.repository = AttrSelectRepository(session)
async def get_options(self, select_id: int) -> GetAllAttrSelectOptionsResponse:
options = await self.repository.get_options(select_id)
return GetAllAttrSelectOptionsResponse(
items=[AttrOptionSchema.model_validate(option) for option in options]
)