feat(db): all domain models, initial Alembic migration (32 tables), seed script (8 districts, 89 regions, roles)

This commit is contained in:
apuc committed 2026-09-26 19:46:44 +03:00
1 parent 71460ab56a
commit 4a610ba94a
19 files changed
+1290 -2

No files matched your search

+108
View File
@@ -0,0 +1,108 @@
"""Модели пользователей, ролей и доступа."""
from datetime import date, datetime
from sqlalchemy import (
Boolean,
Date,
DateTime,
ForeignKey,
String,
UniqueConstraint,
func,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.db.session import Base
from app.models.enums import LoginTokenStatus, MemberType, MessengerType, RoleCode, UserStatus
from app.models.types import enum_col
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
full_name: Mapped[str | None] = mapped_column(String(200))
messenger_type: Mapped[MessengerType] = mapped_column(enum_col(MessengerType))
messenger_id: Mapped[str] = mapped_column(String(64))
phone: Mapped[str | None] = mapped_column(String(32))
status: Mapped[UserStatus] = mapped_column(
enum_col(UserStatus), default=UserStatus.ACTIVE
)
member_type: Mapped[MemberType | None] = mapped_column(enum_col(MemberType))
joined_at: Mapped[date | None] = mapped_column(Date)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
__table_args__ = (UniqueConstraint("messenger_type", "messenger_id", name="uq_user_messenger"),)
regions: Mapped[list["UserRegion"]] = relationship(back_populates="user")
roles: Mapped[list["UserRole"]] = relationship(
back_populates="user", foreign_keys="UserRole.user_id"
)
class UserRegion(Base):
__tablename__ = "user_regions"
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
region_id: Mapped[int] = mapped_column(ForeignKey("regions.id", ondelete="CASCADE"))
is_primary: Mapped[bool] = mapped_column(Boolean, default=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
__table_args__ = (UniqueConstraint("user_id", "region_id", name="uq_user_region"),)
user: Mapped[User] = relationship(back_populates="regions")
class Role(Base):
__tablename__ = "roles"
id: Mapped[int] = mapped_column(primary_key=True)
code: Mapped[RoleCode] = mapped_column(enum_col(RoleCode), unique=True)
name: Mapped[str] = mapped_column(String(100))
class UserRole(Base):
__tablename__ = "user_roles"
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
role_id: Mapped[int] = mapped_column(ForeignKey("roles.id"))
region_id: Mapped[int | None] = mapped_column(ForeignKey("regions.id"))
granted_by: Mapped[int | None] = mapped_column(ForeignKey("users.id"))
granted_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
__table_args__ = (UniqueConstraint("user_id", "role_id", "region_id", name="uq_user_role"),)
user: Mapped[User] = relationship(back_populates="roles", foreign_keys=[user_id])
class LoginToken(Base):
__tablename__ = "login_tokens"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
token: Mapped[str] = mapped_column(String(128), unique=True)
user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"))
region_id: Mapped[int | None] = mapped_column(ForeignKey("regions.id"))
issued_by: Mapped[int | None] = mapped_column(ForeignKey("users.id"))
status: Mapped[LoginTokenStatus] = mapped_column(
enum_col(LoginTokenStatus), default=LoginTokenStatus.ACTIVE
)
expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
used_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
class Session(Base):
__tablename__ = "sessions"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
token_hash: Mapped[str] = mapped_column(String(128), unique=True)
messenger_type: Mapped[MessengerType] = mapped_column(enum_col(MessengerType))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))