From 839fab48f49969372c954679bf209ec3b6a69d2a Mon Sep 17 00:00:00 2001 From: geekiot Date: Mon, 27 Oct 2025 19:38:09 +0500 Subject: [PATCH] Add [database]: add base database models & logic --- src/bot/__init__.py | 4 +- src/database/__init__.py | 4 +- src/database/models.py | 108 ++++++++++++++++++++++++++++++++++++++- src/database/session.py | 42 +++++++++++++-- src/main.py | 9 +++- 5 files changed, 158 insertions(+), 9 deletions(-) diff --git a/src/bot/__init__.py b/src/bot/__init__.py index a8b91d3..ae97876 100644 --- a/src/bot/__init__.py +++ b/src/bot/__init__.py @@ -4,7 +4,7 @@ __all__ = ["start_bot"] from aiogram import Bot, Dispatcher from loguru import logger as loguru_logger from redis.asyncio.client import Redis -from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker from .handlers import include_routers, registry from .middlewares import connect_middlewares @@ -14,7 +14,7 @@ from .middlewares import connect_middlewares async def start_bot( bot_token: str, redis_client: Redis, - database_session: AsyncSession, + database_session: async_sessionmaker[AsyncSession], ) -> None: """ Start Telegram bot. diff --git a/src/database/__init__.py b/src/database/__init__.py index 1727ba2..4c8fbb8 100644 --- a/src/database/__init__.py +++ b/src/database/__init__.py @@ -1,4 +1,4 @@ -__all__ = ["session"] +__all__ = ["create_session", "create_db", "drop_db"] -from .session import session +from .session import create_db, create_session, drop_db diff --git a/src/database/models.py b/src/database/models.py index c855580..cad8f11 100644 --- a/src/database/models.py +++ b/src/database/models.py @@ -1 +1,107 @@ -# TODO: Add database models. +from sqlalchemy import DateTime, ForeignKey, Integer, String, func +from sqlalchemy.orm import ( + DeclarativeBase, + Mapped, + mapped_column, + relationship, +) + + +class Base(DeclarativeBase): + updated: Mapped[DateTime] = mapped_column( + DateTime, + default=func.now(), + onupdate=func.now(), + nullable=False, + ) + + created: Mapped[DateTime] = mapped_column( + DateTime, + default=func.now(), + nullable=False, + ) + + +class UniversityMember(Base): + __tablename__ = "university_members" + + id: Mapped[int] = mapped_column( + Integer, + primary_key=True, + autoincrement=True, + ) + + name: Mapped[str] = mapped_column( + String(128), + nullable=False, + ) + + tg_users = relationship( + "TelegramUser", + back_populates="university_member", + cascade="all, delete-orphan", + ) + + __mapper_args__ = { + "polymorphic_identity": "university_member", + "polymorphic_on": "type", + } + + type: Mapped[str] = mapped_column(String(50)) + + +class Student(UniversityMember): + __tablename__ = "students" + + id: Mapped[int] = mapped_column( + Integer, + ForeignKey("university_members.id"), + primary_key=True, + ) + + student_id: Mapped[int] = mapped_column( + Integer, + unique=True, + nullable=False, + ) + + group_id: Mapped[int] = mapped_column( + Integer, + nullable=True, + ) + + __mapper_args__ = { + "polymorphic_identity": "student", + } + + +class TelegramUser(Base): + __tablename__ = "telegram_users" + + id: Mapped[int] = mapped_column( + Integer, + primary_key=True, + autoincrement=False, + ) + + username: Mapped[str] = mapped_column( + String(64), + nullable=True, + ) + + lang: Mapped[str] = mapped_column( + String(2), + default="ru", + nullable=False, + ) + + university_member_id: Mapped[int] = mapped_column( + Integer, + ForeignKey("university_members.id"), + nullable=True, + ) + + university_member = relationship( + "UniversityMember", + back_populates="tg_users", + ) diff --git a/src/database/session.py b/src/database/session.py index abdcd2d..4fd389c 100644 --- a/src/database/session.py +++ b/src/database/session.py @@ -1,4 +1,40 @@ -from sqlalchemy.ext.asyncio import AsyncSession +# from loguru import logger as loguru_logger +from sqlalchemy.ext.asyncio import ( + AsyncEngine, + AsyncSession, + async_sessionmaker, + create_async_engine, +) -# TODO: Add database session. -session: AsyncSession = ... +from .models import Base + +engine: AsyncEngine + + +def create_session( + url: str, engine_echo: bool = True +) -> async_sessionmaker[AsyncSession]: + global engine + engine = create_async_engine(url=url, echo=engine_echo) + + session = async_sessionmaker( + bind=engine, + class_=AsyncSession, + expire_on_commit=False, + ) + + return session + + +async def create_db(): + global engine + + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) + + +async def drop_db(): + global engine + + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.drop_all) diff --git a/src/main.py b/src/main.py index 824ed22..c738f16 100644 --- a/src/main.py +++ b/src/main.py @@ -2,7 +2,7 @@ import asyncio from bot import start_bot from config import configure_logger, settings -from database import session +from database import create_db, create_session from redis_client import client @@ -17,6 +17,13 @@ def main() -> None: listen_logging=settings.listen_logging, ) + session = create_session( + url=settings.database_url, engine_echo=True + ) + + # FIXME: Add argument for create/drop db. + asyncio.run(create_db()) + asyncio.run( start_bot( bot_token=settings.bot_token,