Products Rendering, removal of deprecated files
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -173,3 +173,6 @@ cython_debug/
|
|||||||
|
|
||||||
# PyPI configuration file
|
# PyPI configuration file
|
||||||
.pypirc
|
.pypirc
|
||||||
|
|
||||||
|
# Static / IMG
|
||||||
|
static/img
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
"""file id not null constraint edited -> true
|
||||||
|
|
||||||
|
Revision ID: 2c9553f99e03
|
||||||
|
Revises: e9de494cf92c
|
||||||
|
Create Date: 2026-02-06 19:39:28.107493
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "2c9553f99e03"
|
||||||
|
down_revision: Union[str, Sequence[str], None] = "e9de494cf92c"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.alter_column("products", "file_id", existing_type=sa.TEXT(), nullable=True)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.alter_column("products", "file_id", existing_type=sa.TEXT(), nullable=False)
|
||||||
|
# ### end Alembic commands ###
|
||||||
38
alembic/versions/e9de494cf92c_img_path_file_id_added.py
Normal file
38
alembic/versions/e9de494cf92c_img_path_file_id_added.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
"""Img Path + File Id added
|
||||||
|
|
||||||
|
Revision ID: e9de494cf92c
|
||||||
|
Revises: 8756e344268a
|
||||||
|
Create Date: 2026-02-06 19:19:44.437326
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "e9de494cf92c"
|
||||||
|
down_revision: Union[str, Sequence[str], None] = "8756e344268a"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column("products", sa.Column("img_path", sa.Text(), nullable=False))
|
||||||
|
op.add_column("products", sa.Column("file_id", sa.Text(), nullable=False))
|
||||||
|
op.create_unique_constraint(None, "products", ["file_id"])
|
||||||
|
op.create_unique_constraint(None, "products", ["img_path"])
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_constraint(None, "products", type_="unique")
|
||||||
|
op.drop_constraint(None, "products", type_="unique")
|
||||||
|
op.drop_column("products", "file_id")
|
||||||
|
op.drop_column("products", "img_path")
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -5,9 +5,15 @@ from dotenv import load_dotenv
|
|||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
### Environment Variables ###
|
||||||
|
|
||||||
BOT_TOKEN = os.getenv("BOT_TOKEN", "")
|
BOT_TOKEN = os.getenv("BOT_TOKEN", "")
|
||||||
|
|
||||||
POSTGRES_URL = os.getenv(
|
POSTGRES_URL = os.getenv(
|
||||||
"POSTGRES_URL",
|
"POSTGRES_URL",
|
||||||
"postgresql+asyncpg://postgres:HEXDEVFUCKINGSUCKS!@localhost:5432/postgres",
|
"postgresql+asyncpg://postgres:HEXDEVFUCKINGSUCKS!@localhost:5432/postgres",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
### Constants ###
|
||||||
|
|
||||||
|
PAGE_SIZE = 5
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ class Product(Base):
|
|||||||
name: Mapped[str] = mapped_column(Text, nullable=False)
|
name: Mapped[str] = mapped_column(Text, nullable=False)
|
||||||
description: Mapped[Optional[str]] = mapped_column(Text)
|
description: Mapped[Optional[str]] = mapped_column(Text)
|
||||||
price: Mapped[int] = mapped_column()
|
price: Mapped[int] = mapped_column()
|
||||||
|
img_path: Mapped[str] = mapped_column(Text, nullable=False, unique=True)
|
||||||
|
file_id: Mapped[str] = mapped_column(Text, nullable=True, unique=True)
|
||||||
|
|
||||||
search_vector: Mapped[str] = mapped_column(TSVECTOR)
|
search_vector: Mapped[str] = mapped_column(TSVECTOR)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
|
||||||
class CategoryDTO:
|
|
||||||
id: int
|
|
||||||
name: str
|
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
|
from re import Match
|
||||||
from aiogram import Router, F
|
from aiogram import Router, F
|
||||||
from aiogram.types import CallbackQuery, Message
|
from aiogram.types import CallbackQuery, Message
|
||||||
from aiogram.filters import Command
|
from aiogram.filters import Command
|
||||||
from aiogram.fsm.context import FSMContext
|
from aiogram.fsm.context import FSMContext
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from config import PAGE_SIZE
|
||||||
from misc.kb import main_menu_kb, render_category
|
from misc.kb import main_menu_kb, render_category
|
||||||
|
from misc.kb.client import get_back_to_catalogue, render_products
|
||||||
from repositories.categories import CategoriesRepository
|
from repositories.categories import CategoriesRepository
|
||||||
from services.categories import CategoryService
|
from repositories.products import ProductRepository
|
||||||
from services.orders import OrderService
|
from services.orders import OrderService
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
@@ -17,7 +20,7 @@ async def main_menu(msg: Message, state: FSMContext):
|
|||||||
await state.clear()
|
await state.clear()
|
||||||
|
|
||||||
await msg.answer(
|
await msg.answer(
|
||||||
"hii!", reply_markup=await main_menu_kb(msg.from_user.id)
|
"hii!", reply_markup=main_menu_kb(msg.from_user.id)
|
||||||
) # TODO: Write a welcome message
|
) # TODO: Write a welcome message
|
||||||
|
|
||||||
|
|
||||||
@@ -26,7 +29,7 @@ async def main_menu_cb(cb: CallbackQuery, state: FSMContext):
|
|||||||
await state.clear()
|
await state.clear()
|
||||||
|
|
||||||
await cb.message.edit_text(
|
await cb.message.edit_text(
|
||||||
"hii!", reply_markup=await main_menu_kb(cb.from_user.id)
|
"hii!", reply_markup=main_menu_kb(cb.from_user.id)
|
||||||
) # TODO: Write a welcome message
|
) # TODO: Write a welcome message
|
||||||
|
|
||||||
|
|
||||||
@@ -47,27 +50,91 @@ async def get_orders(
|
|||||||
async def subcatalogue(
|
async def subcatalogue(
|
||||||
cb: CallbackQuery,
|
cb: CallbackQuery,
|
||||||
state: FSMContext,
|
state: FSMContext,
|
||||||
category_service: CategoryService,
|
|
||||||
session: AsyncSession,
|
session: AsyncSession,
|
||||||
categories_repo: CategoriesRepository,
|
categories_repo: CategoriesRepository,
|
||||||
|
products_repo: ProductRepository,
|
||||||
):
|
):
|
||||||
await state.clear()
|
await state.clear()
|
||||||
|
|
||||||
cat_id = cb.data.split(":")[1]
|
cat_id = cb.data.split(":")[1]
|
||||||
|
|
||||||
if cat_id == "root":
|
if cat_id == "root":
|
||||||
children = await category_service.get_children(session)
|
children = await categories_repo.get_categories_by_parent_id(session)
|
||||||
kb = await render_category(children, show_menu=True)
|
kb = render_category(children, show_menu=True)
|
||||||
|
await cb.message.edit_text(
|
||||||
|
"category selection",
|
||||||
|
reply_markup=kb,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
else:
|
|
||||||
cat_id = int(cat_id)
|
cat_id = int(cat_id)
|
||||||
|
|
||||||
children = await category_service.get_children(session, cat_id)
|
children = await categories_repo.get_categories_by_parent_id(session, cat_id)
|
||||||
category = await categories_repo.get_category_by_id(session, cat_id)
|
category = await categories_repo.get_category_by_id(session, cat_id)
|
||||||
parent_id = category.parent_id or "root"
|
parent_id = category.parent_id or "root"
|
||||||
kb = await render_category(children, parent_id)
|
|
||||||
|
if not children:
|
||||||
|
products = await products_repo.get_product_by_category(
|
||||||
|
session, cat_id, limit=PAGE_SIZE + 1, offset=0
|
||||||
|
)
|
||||||
|
products_len = len(products)
|
||||||
|
|
||||||
|
if not products:
|
||||||
|
await cb.message.edit_text(
|
||||||
|
"🍃 Здесь ничего нет...",
|
||||||
|
reply_markup=get_back_to_catalogue(parent_id),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
await cb.message.edit_text(
|
||||||
|
"hya",
|
||||||
|
reply_markup=render_products(
|
||||||
|
products[:PAGE_SIZE],
|
||||||
|
cat_id=cat_id,
|
||||||
|
page=0,
|
||||||
|
show_prev=products_len < 0,
|
||||||
|
show_next=products_len > PAGE_SIZE,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
kb = render_category(children, parent_id)
|
||||||
|
|
||||||
await cb.message.edit_text(
|
await cb.message.edit_text(
|
||||||
"category selection",
|
"category selection",
|
||||||
reply_markup=kb,
|
reply_markup=kb,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(
|
||||||
|
F.data.regexp(r"products:(?P<category_id>.*):(?P<page_id>\d+)").as_(
|
||||||
|
"pagination_match"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
async def products_pagination(
|
||||||
|
cb: CallbackQuery,
|
||||||
|
state: FSMContext,
|
||||||
|
pagination_match: Match[str],
|
||||||
|
session: AsyncSession,
|
||||||
|
products_repo: ProductRepository,
|
||||||
|
):
|
||||||
|
await state.clear()
|
||||||
|
|
||||||
|
pagination_data = pagination_match.groupdict()
|
||||||
|
category_id = int(pagination_data.get("category_id"))
|
||||||
|
page_id = int(pagination_data.get("page_id", 0))
|
||||||
|
|
||||||
|
products = await products_repo.get_product_by_category(
|
||||||
|
session, category_id, limit=PAGE_SIZE + 1, offset=page_id * PAGE_SIZE
|
||||||
|
)
|
||||||
|
products_len = len(products)
|
||||||
|
|
||||||
|
await cb.message.edit_reply_markup(
|
||||||
|
reply_markup=render_products(
|
||||||
|
products[:PAGE_SIZE],
|
||||||
|
cat_id=category_id,
|
||||||
|
page=page_id,
|
||||||
|
show_prev=page_id > 0,
|
||||||
|
show_next=products_len > PAGE_SIZE,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|||||||
19
main.py
19
main.py
@@ -1,3 +1,4 @@
|
|||||||
|
import os
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from aiogram import Bot, Dispatcher
|
from aiogram import Bot, Dispatcher
|
||||||
@@ -10,14 +11,15 @@ from middlewares.repository import RepositoryMiddleware
|
|||||||
from middlewares.session import DBSessionMiddleware
|
from middlewares.session import DBSessionMiddleware
|
||||||
from repositories.categories import CategoriesRepository
|
from repositories.categories import CategoriesRepository
|
||||||
from repositories.orders import OrderRepository
|
from repositories.orders import OrderRepository
|
||||||
|
from repositories.products import ProductRepository
|
||||||
from services import OrderService
|
from services import OrderService
|
||||||
from db.base import async_session
|
from db.base import async_session
|
||||||
from services.categories import CategoryService
|
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
os.makedirs("static/img", exist_ok=True)
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
dp = Dispatcher()
|
dp = Dispatcher()
|
||||||
@@ -29,16 +31,21 @@ async def main():
|
|||||||
order_service = OrderService(order_repository)
|
order_service = OrderService(order_repository)
|
||||||
|
|
||||||
category_repository = CategoriesRepository()
|
category_repository = CategoriesRepository()
|
||||||
category_service = CategoryService(category_repository)
|
|
||||||
|
product_repository = ProductRepository()
|
||||||
|
|
||||||
# DI Middleware Init
|
# DI Middleware Init
|
||||||
dp.message.middleware(DIMiddleware(order_service, category_service))
|
dp.message.middleware(DIMiddleware(order_service))
|
||||||
dp.message.middleware(DBSessionMiddleware(async_session))
|
dp.message.middleware(DBSessionMiddleware(async_session))
|
||||||
dp.message.middleware(RepositoryMiddleware())
|
dp.message.middleware(
|
||||||
|
RepositoryMiddleware(order_repository, category_repository, product_repository)
|
||||||
|
)
|
||||||
|
|
||||||
dp.callback_query.middleware(DIMiddleware(order_service, category_service))
|
dp.callback_query.middleware(DIMiddleware(order_service))
|
||||||
dp.callback_query.middleware(DBSessionMiddleware(async_session))
|
dp.callback_query.middleware(DBSessionMiddleware(async_session))
|
||||||
dp.callback_query.middleware(RepositoryMiddleware())
|
dp.callback_query.middleware(
|
||||||
|
RepositoryMiddleware(order_repository, category_repository, product_repository)
|
||||||
|
)
|
||||||
|
|
||||||
init_handlers(dp)
|
init_handlers(dp)
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
from aiogram import BaseMiddleware
|
from aiogram import BaseMiddleware
|
||||||
from typing import Any, Callable
|
from typing import Any, Callable
|
||||||
|
|
||||||
from services.categories import CategoryService
|
|
||||||
from services.orders import OrderService
|
from services.orders import OrderService
|
||||||
|
|
||||||
|
|
||||||
class DIMiddleware(BaseMiddleware):
|
class DIMiddleware(BaseMiddleware):
|
||||||
def __init__(self, order_service: OrderService, category_service: CategoryService):
|
def __init__(self, order_service: OrderService):
|
||||||
self._order_service = order_service
|
self._order_service = order_service
|
||||||
self._category_service = category_service
|
|
||||||
|
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
@@ -17,6 +15,5 @@ class DIMiddleware(BaseMiddleware):
|
|||||||
data: dict[str, Any],
|
data: dict[str, Any],
|
||||||
) -> Any:
|
) -> Any:
|
||||||
data["order_service"] = self._order_service
|
data["order_service"] = self._order_service
|
||||||
data["category_service"] = self._category_service
|
|
||||||
|
|
||||||
return await handler(event, data)
|
return await handler(event, data)
|
||||||
|
|||||||
@@ -1,10 +1,22 @@
|
|||||||
from aiogram import BaseMiddleware
|
from aiogram import BaseMiddleware
|
||||||
|
|
||||||
from repositories import OrderRepository, CategoriesRepository
|
from repositories import OrderRepository, CategoriesRepository
|
||||||
|
from repositories.products import ProductRepository
|
||||||
|
|
||||||
|
|
||||||
class RepositoryMiddleware(BaseMiddleware):
|
class RepositoryMiddleware(BaseMiddleware):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
order_repo: OrderRepository,
|
||||||
|
categories_repo: CategoriesRepository,
|
||||||
|
products_repo: ProductRepository,
|
||||||
|
) -> None:
|
||||||
|
self._order_repo = order_repo
|
||||||
|
self._categories_repo = categories_repo
|
||||||
|
self._products_repo = products_repo
|
||||||
|
|
||||||
async def __call__(self, handler, event, data):
|
async def __call__(self, handler, event, data):
|
||||||
data["order_repo"] = OrderRepository()
|
data["order_repo"] = self._order_repo
|
||||||
data["categories_repo"] = CategoriesRepository()
|
data["categories_repo"] = self._categories_repo
|
||||||
|
data["products_repo"] = self._products_repo
|
||||||
return await handler(event, data)
|
return await handler(event, data)
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ from typing import Optional
|
|||||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||||
|
|
||||||
from dto.categories import CategoryDTO
|
from db.models import Category
|
||||||
|
from db.models.products import Product
|
||||||
|
|
||||||
|
|
||||||
def cart_btn(user_id: int) -> InlineKeyboardButton:
|
def cart_btn(user_id: int) -> InlineKeyboardButton:
|
||||||
@@ -12,13 +13,15 @@ def cart_btn(user_id: int) -> InlineKeyboardButton:
|
|||||||
) # TODO: Database integration, get cart items count
|
) # TODO: Database integration, get cart items count
|
||||||
|
|
||||||
|
|
||||||
def get_back_btn(
|
def get_back_to_catalogue(
|
||||||
data: str = "menu:main", text: Optional[str] = "⬅️ Назад"
|
cat_id: int, *, text: Optional[str] = "⬅️ Назад"
|
||||||
) -> InlineKeyboardButton:
|
) -> InlineKeyboardMarkup:
|
||||||
return InlineKeyboardButton(text=text, callback_data=data)
|
return InlineKeyboardBuilder(
|
||||||
|
[[InlineKeyboardButton(text=text, callback_data=f"cat:{cat_id}")]]
|
||||||
|
).as_markup()
|
||||||
|
|
||||||
|
|
||||||
async def main_menu_kb(user_id: int) -> InlineKeyboardMarkup:
|
def main_menu_kb(user_id: int) -> InlineKeyboardMarkup:
|
||||||
return InlineKeyboardBuilder(
|
return InlineKeyboardBuilder(
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
@@ -30,8 +33,8 @@ async def main_menu_kb(user_id: int) -> InlineKeyboardMarkup:
|
|||||||
).as_markup()
|
).as_markup()
|
||||||
|
|
||||||
|
|
||||||
async def render_category(
|
def render_category(
|
||||||
children: list[CategoryDTO],
|
children: list[Category],
|
||||||
parent_id: Optional[int] = None,
|
parent_id: Optional[int] = None,
|
||||||
show_menu: bool = False,
|
show_menu: bool = False,
|
||||||
) -> InlineKeyboardMarkup:
|
) -> InlineKeyboardMarkup:
|
||||||
@@ -56,3 +59,48 @@ async def render_category(
|
|||||||
return InlineKeyboardBuilder(
|
return InlineKeyboardBuilder(
|
||||||
[btns[i : i + 2] for i in range(0, len(children), 2)] + [nav],
|
[btns[i : i + 2] for i in range(0, len(children), 2)] + [nav],
|
||||||
).as_markup()
|
).as_markup()
|
||||||
|
|
||||||
|
|
||||||
|
def render_products(
|
||||||
|
products: list[Product],
|
||||||
|
*,
|
||||||
|
cat_id: int,
|
||||||
|
page: int = 0,
|
||||||
|
show_prev: Optional[bool] = False,
|
||||||
|
show_next: Optional[bool] = False,
|
||||||
|
) -> InlineKeyboardMarkup:
|
||||||
|
btns = [
|
||||||
|
[InlineKeyboardButton(text=product.name, callback_data=f"product:{product.id}")]
|
||||||
|
for product in products
|
||||||
|
]
|
||||||
|
|
||||||
|
nav = []
|
||||||
|
|
||||||
|
if show_prev:
|
||||||
|
nav.append(
|
||||||
|
InlineKeyboardButton(
|
||||||
|
text="⬅️", callback_data=f"products:{cat_id}:{page - 1}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
nav.append(InlineKeyboardButton(text=str(page), callback_data="..."))
|
||||||
|
|
||||||
|
if show_next:
|
||||||
|
nav.append(
|
||||||
|
InlineKeyboardButton(
|
||||||
|
text="➡️", callback_data=f"products:{cat_id}:{page + 1}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return InlineKeyboardBuilder(
|
||||||
|
[
|
||||||
|
[
|
||||||
|
InlineKeyboardButton(
|
||||||
|
text="⬅️ Назад",
|
||||||
|
callback_data=f"cat:{cat_id}",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
+ btns
|
||||||
|
+ [nav]
|
||||||
|
).as_markup()
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
docker start test-postgres
|
docker run --name test-postgres -e POSTGRES_PASSWORD=HEXDEVFUCKINGSUCKS! -p 5432:5432 -d postgres
|
||||||
pg_isready -h localhost -p 5432
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
from .orders import OrderRepository
|
from .orders import OrderRepository
|
||||||
from .categories import CategoriesRepository
|
from .categories import CategoriesRepository
|
||||||
|
from .products import ProductRepository
|
||||||
|
|
||||||
__all__ = ["OrderRepository", "CategoriesRepository"]
|
__all__ = ["OrderRepository", "CategoriesRepository", "ProductRepository"]
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class CategoriesRepository:
|
|||||||
stmt = select(Category).where(Category.id == category_id)
|
stmt = select(Category).where(Category.id == category_id)
|
||||||
return await session.scalar(stmt)
|
return await session.scalar(stmt)
|
||||||
|
|
||||||
async def get_category_by_parent_id(
|
async def get_categories_by_parent_id(
|
||||||
self, session: AsyncSession, parent_id: Optional[int] = None
|
self, session: AsyncSession, parent_id: Optional[int] = None
|
||||||
) -> list[Category]:
|
) -> list[Category]:
|
||||||
stmt = select(Category).where(Category.parent_id == parent_id)
|
stmt = select(Category).where(Category.parent_id == parent_id)
|
||||||
|
|||||||
28
repositories/products.py
Normal file
28
repositories/products.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from sqlalchemy import select
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from db.models import Product
|
||||||
|
|
||||||
|
|
||||||
|
class ProductRepository:
|
||||||
|
async def get_product_by_id(
|
||||||
|
self, session: AsyncSession, product_id: int
|
||||||
|
) -> Optional[Product]:
|
||||||
|
stmt = select(Product).where(Product.id == product_id)
|
||||||
|
return await session.scalar(stmt)
|
||||||
|
|
||||||
|
async def get_product_by_category(
|
||||||
|
self, session: AsyncSession, category_id: int, *, limit: int, offset: int
|
||||||
|
) -> list[Product]:
|
||||||
|
stmt = (
|
||||||
|
select(Product)
|
||||||
|
.where(Product.category_id == category_id)
|
||||||
|
.order_by(Product.id)
|
||||||
|
.offset(offset)
|
||||||
|
.limit(limit)
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await session.scalars(stmt)
|
||||||
|
|
||||||
|
return list(result)
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
from typing import Optional
|
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
|
||||||
|
|
||||||
from dto.categories import CategoryDTO
|
|
||||||
from repositories import CategoriesRepository
|
|
||||||
|
|
||||||
|
|
||||||
class CategoryService:
|
|
||||||
def __init__(self, category_repo: CategoriesRepository):
|
|
||||||
self.category_repo = category_repo
|
|
||||||
|
|
||||||
async def get_children(
|
|
||||||
self, session: AsyncSession, parent_id: Optional[int] = None
|
|
||||||
) -> list[CategoryDTO]:
|
|
||||||
result = await self.category_repo.get_category_by_parent_id(session, parent_id)
|
|
||||||
return [
|
|
||||||
CategoryDTO(x.id, x.name) for x in result
|
|
||||||
] # FIXME: Optimization may be required here - O(n)
|
|
||||||
Reference in New Issue
Block a user