модельки для проекта Лены
This commit is contained in:
@@ -9,3 +9,9 @@ remove:
|
|||||||
|
|
||||||
dev:
|
dev:
|
||||||
uvicorn app.main:app --reload
|
uvicorn app.main:app --reload
|
||||||
|
|
||||||
|
makemigration:
|
||||||
|
alembic revision --autogenerate
|
||||||
|
|
||||||
|
migrate:
|
||||||
|
alembic upgrade head
|
||||||
|
|||||||
@@ -1,6 +1,26 @@
|
|||||||
from app.dao.base import BaseDAO
|
from app.dao.base import BaseDAO
|
||||||
from app.auth.models import User
|
from app.models import *
|
||||||
|
|
||||||
|
|
||||||
class UsersDAO(BaseDAO):
|
class DeansDAO(BaseDAO):
|
||||||
model = User
|
model = Dean
|
||||||
|
|
||||||
|
|
||||||
|
class FacultsDAO(BaseDAO):
|
||||||
|
model = Facult
|
||||||
|
|
||||||
|
|
||||||
|
class StudentsDAO(BaseDAO):
|
||||||
|
model = Student
|
||||||
|
|
||||||
|
|
||||||
|
class SpecializationsDAO(BaseDAO):
|
||||||
|
model = Specialization
|
||||||
|
|
||||||
|
|
||||||
|
class DeansDAO(BaseDAO):
|
||||||
|
model = Dean
|
||||||
|
|
||||||
|
|
||||||
|
class GroupsDAO(BaseDAO):
|
||||||
|
model = Group
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
# flake8: noqa
|
|
||||||
from .user import User
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
from sqlalchemy import String
|
|
||||||
from sqlalchemy.orm import Mapped, mapped_column
|
|
||||||
from app.dao.database import Base
|
|
||||||
|
|
||||||
|
|
||||||
class User(Base):
|
|
||||||
first_name: Mapped[str] = mapped_column(String(50))
|
|
||||||
last_name: Mapped[str] = mapped_column(String(50))
|
|
||||||
email: Mapped[str] = mapped_column(String(50), unique=True)
|
|
||||||
password: Mapped[str] = mapped_column(String(256))
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"{self.__class__.__name__}(id={self.id})"
|
|
||||||
@@ -2,7 +2,7 @@ from typing import List
|
|||||||
from fastapi import APIRouter, Response, Depends
|
from fastapi import APIRouter, Response, Depends
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.auth.models import User
|
from app.models import Dean
|
||||||
from app.auth.utils import authenticate_user, set_tokens
|
from app.auth.utils import authenticate_user, set_tokens
|
||||||
from app.dependencies.auth_dep import (
|
from app.dependencies.auth_dep import (
|
||||||
get_current_user,
|
get_current_user,
|
||||||
@@ -10,18 +10,18 @@ from app.dependencies.auth_dep import (
|
|||||||
)
|
)
|
||||||
from app.dependencies.dao_dep import get_session_with_commit, get_session_without_commit
|
from app.dependencies.dao_dep import get_session_with_commit, get_session_without_commit
|
||||||
from app.exceptions import UserAlreadyExistsException, IncorrectEmailOrPasswordException
|
from app.exceptions import UserAlreadyExistsException, IncorrectEmailOrPasswordException
|
||||||
from app.auth.dao import UsersDAO
|
from app.auth.dao import DeansDAO
|
||||||
from app.auth.schemas import SUserRegister, SUserAuth, EmailModel, SUserAddDB, SUserInfo
|
from app.auth.schemas import SDeanRegister, SDeanAuth, EmailModel, SDeanAddDB, SDeanInfo
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/register/")
|
@router.post("/register/")
|
||||||
async def register_user(
|
async def register_user(
|
||||||
user_data: SUserRegister, session: AsyncSession = Depends(get_session_with_commit)
|
user_data: SDeanRegister, session: AsyncSession = Depends(get_session_with_commit)
|
||||||
) -> dict:
|
) -> dict:
|
||||||
# Проверка существования пользователя
|
# Проверка существования пользователя
|
||||||
user_dao = UsersDAO(session)
|
user_dao = DeansDAO(session)
|
||||||
|
|
||||||
existing_user = await user_dao.find_one_or_none(
|
existing_user = await user_dao.find_one_or_none(
|
||||||
filters=EmailModel(email=user_data.email)
|
filters=EmailModel(email=user_data.email)
|
||||||
@@ -34,7 +34,7 @@ async def register_user(
|
|||||||
user_data_dict.pop("confirm_password", None)
|
user_data_dict.pop("confirm_password", None)
|
||||||
|
|
||||||
# Добавление пользователя
|
# Добавление пользователя
|
||||||
await user_dao.add(values=SUserAddDB(**user_data_dict))
|
await user_dao.add(values=SDeanAddDB(**user_data_dict))
|
||||||
|
|
||||||
return {"message": "Вы успешно зарегистрированы!"}
|
return {"message": "Вы успешно зарегистрированы!"}
|
||||||
|
|
||||||
@@ -42,10 +42,10 @@ async def register_user(
|
|||||||
@router.post("/login/")
|
@router.post("/login/")
|
||||||
async def auth_user(
|
async def auth_user(
|
||||||
response: Response,
|
response: Response,
|
||||||
user_data: SUserAuth,
|
user_data: SDeanAuth,
|
||||||
session: AsyncSession = Depends(get_session_without_commit),
|
session: AsyncSession = Depends(get_session_without_commit),
|
||||||
) -> dict:
|
) -> dict:
|
||||||
users_dao = UsersDAO(session)
|
users_dao = DeansDAO(session)
|
||||||
user = await users_dao.find_one_or_none(filters=EmailModel(email=user_data.email))
|
user = await users_dao.find_one_or_none(filters=EmailModel(email=user_data.email))
|
||||||
|
|
||||||
if not (user and await authenticate_user(user=user, password=user_data.password)):
|
if not (user and await authenticate_user(user=user, password=user_data.password)):
|
||||||
@@ -62,13 +62,13 @@ async def logout(response: Response):
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/me/")
|
@router.get("/me/")
|
||||||
async def get_me(user_data: User = Depends(get_current_user)) -> SUserInfo:
|
async def get_me(user_data: Dean = Depends(get_current_user)) -> SDeanInfo:
|
||||||
return SUserInfo.model_validate(user_data)
|
return SDeanInfo.model_validate(user_data)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/refresh")
|
@router.post("/refresh")
|
||||||
async def process_refresh_token(
|
async def process_refresh_token(
|
||||||
response: Response, user: User = Depends(check_refresh_token)
|
response: Response, user: Dean = Depends(check_refresh_token)
|
||||||
):
|
):
|
||||||
set_tokens(response, user.id)
|
set_tokens(response, user.id)
|
||||||
return {"message": "Токены успешно обновлены"}
|
return {"message": "Токены успешно обновлены"}
|
||||||
|
|||||||
@@ -15,15 +15,21 @@ class EmailModel(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class UserBase(EmailModel):
|
class UserBase(EmailModel):
|
||||||
first_name: str = Field(
|
name: str = Field(
|
||||||
min_length=3, max_length=50, description="Имя, от 3 до 50 символов"
|
min_length=3, max_length=50, description="Имя, от 3 до 50 символов"
|
||||||
)
|
)
|
||||||
last_name: str = Field(
|
surname: str = Field(
|
||||||
min_length=3, max_length=50, description="Фамилия, от 3 до 50 символов"
|
min_length=3, max_length=50, description="Фамилия, от 3 до 50 символов"
|
||||||
)
|
)
|
||||||
|
patronymic: str = Field(
|
||||||
|
min_length=3,
|
||||||
|
max_length=50,
|
||||||
|
description="Отчество, от 3 до 50 символов",
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SUserRegister(UserBase):
|
class SDeanRegister(UserBase):
|
||||||
password: str = Field(
|
password: str = Field(
|
||||||
min_length=5, max_length=50, description="Пароль, от 5 до 50 знаков"
|
min_length=5, max_length=50, description="Пароль, от 5 до 50 знаков"
|
||||||
)
|
)
|
||||||
@@ -41,15 +47,15 @@ class SUserRegister(UserBase):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
class SUserAddDB(UserBase):
|
class SDeanAddDB(UserBase):
|
||||||
password: str = Field(min_length=5, description="Пароль в формате HASH-строки")
|
password: str = Field(min_length=5, description="Пароль в формате HASH-строки")
|
||||||
|
|
||||||
|
|
||||||
class SUserAuth(EmailModel):
|
class SDeanAuth(EmailModel):
|
||||||
password: str = Field(
|
password: str = Field(
|
||||||
min_length=5, max_length=50, description="Пароль, от 5 до 50 знаков"
|
min_length=5, max_length=50, description="Пароль, от 5 до 50 знаков"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SUserInfo(UserBase):
|
class SDeanInfo(UserBase):
|
||||||
id: int = Field(description="Идентификатор пользователя")
|
id: int = Field(description="Идентификатор пользователя")
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import uuid
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
from sqlalchemy import func, TIMESTAMP, Integer, inspect
|
from sqlalchemy import Integer, inspect
|
||||||
from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase, declared_attr
|
from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase, declared_attr
|
||||||
from sqlalchemy.ext.asyncio import (
|
from sqlalchemy.ext.asyncio import (
|
||||||
AsyncAttrs,
|
AsyncAttrs,
|
||||||
@@ -24,10 +24,6 @@ class Base(AsyncAttrs, DeclarativeBase):
|
|||||||
__abstract__ = True
|
__abstract__ = True
|
||||||
|
|
||||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=func.now())
|
|
||||||
updated_at: Mapped[datetime] = mapped_column(
|
|
||||||
TIMESTAMP, server_default=func.now(), onupdate=func.now()
|
|
||||||
)
|
|
||||||
|
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __tablename__(cls) -> str:
|
def __tablename__(cls) -> str:
|
||||||
@@ -63,6 +59,4 @@ class Base(AsyncAttrs, DeclarativeBase):
|
|||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""Строковое представление объекта для удобства отладки."""
|
"""Строковое представление объекта для удобства отладки."""
|
||||||
return f"<{self.__class__.__name__}(id={self.id}, "
|
return f"<{self.__class__.__name__}(id={self.id})>"
|
||||||
|
|
||||||
"created_at={self.created_at}, updated_at={self.updated_at})>"
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ from fastapi import Request, Depends
|
|||||||
from jose import jwt, JWTError, ExpiredSignatureError
|
from jose import jwt, JWTError, ExpiredSignatureError
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.auth.dao import UsersDAO
|
from app.auth.dao import DeansDAO
|
||||||
from app.auth.models import User
|
from app.models import Dean
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.dependencies.dao_dep import get_session_without_commit
|
from app.dependencies.dao_dep import get_session_without_commit
|
||||||
from app.exceptions import (
|
from app.exceptions import (
|
||||||
@@ -36,7 +36,7 @@ def get_refresh_token(request: Request) -> str:
|
|||||||
async def check_refresh_token(
|
async def check_refresh_token(
|
||||||
token: str = Depends(get_refresh_token),
|
token: str = Depends(get_refresh_token),
|
||||||
session: AsyncSession = Depends(get_session_without_commit),
|
session: AsyncSession = Depends(get_session_without_commit),
|
||||||
) -> User:
|
) -> Dean:
|
||||||
"""Проверяем refresh_token и возвращаем пользователя."""
|
"""Проверяем refresh_token и возвращаем пользователя."""
|
||||||
try:
|
try:
|
||||||
payload = jwt.decode(
|
payload = jwt.decode(
|
||||||
@@ -46,7 +46,7 @@ async def check_refresh_token(
|
|||||||
if not user_id:
|
if not user_id:
|
||||||
raise NoJwtException
|
raise NoJwtException
|
||||||
|
|
||||||
user = await UsersDAO(session).find_one_or_none_by_id(data_id=int(user_id))
|
user = await DeansDAO(session).find_one_or_none_by_id(data_id=int(user_id))
|
||||||
if not user:
|
if not user:
|
||||||
raise NoJwtException
|
raise NoJwtException
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ async def check_refresh_token(
|
|||||||
async def get_current_user(
|
async def get_current_user(
|
||||||
token: str = Depends(get_access_token),
|
token: str = Depends(get_access_token),
|
||||||
session: AsyncSession = Depends(get_session_without_commit),
|
session: AsyncSession = Depends(get_session_without_commit),
|
||||||
) -> User:
|
) -> Dean:
|
||||||
"""Проверяем access_token и возвращаем пользователя."""
|
"""Проверяем access_token и возвращаем пользователя."""
|
||||||
try:
|
try:
|
||||||
# Декодируем токен
|
# Декодируем токен
|
||||||
@@ -80,15 +80,15 @@ async def get_current_user(
|
|||||||
if not user_id:
|
if not user_id:
|
||||||
raise NoUserIdException
|
raise NoUserIdException
|
||||||
|
|
||||||
user = await UsersDAO(session).find_one_or_none_by_id(data_id=int(user_id))
|
user = await DeansDAO(session).find_one_or_none_by_id(data_id=int(user_id))
|
||||||
if not user:
|
if not user:
|
||||||
raise UserNotFoundException
|
raise UserNotFoundException
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
async def get_current_admin_user(
|
async def get_current_admin_user(
|
||||||
current_user: User = Depends(get_current_user),
|
current_user: Dean = Depends(get_current_user),
|
||||||
) -> User:
|
) -> Dean:
|
||||||
"""Проверяем права пользователя как администратора."""
|
"""Проверяем права пользователя как администратора."""
|
||||||
if current_user.role.id in [3, 4]:
|
if current_user.role.id in [3, 4]:
|
||||||
return current_user
|
return current_user
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from sqlalchemy.ext.asyncio import async_engine_from_config
|
|||||||
from alembic import context
|
from alembic import context
|
||||||
from app.config import database_url
|
from app.config import database_url
|
||||||
from app.dao.database import Base
|
from app.dao.database import Base
|
||||||
from app.auth.models import *
|
from app.models import *
|
||||||
|
|
||||||
config = context.config
|
config = context.config
|
||||||
config.set_main_option("sqlalchemy.url", database_url)
|
config.set_main_option("sqlalchemy.url", database_url)
|
||||||
|
|||||||
78
backend/app/migration/versions/cae942c398cb_.py
Normal file
78
backend/app/migration/versions/cae942c398cb_.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: cae942c398cb
|
||||||
|
Revises:
|
||||||
|
Create Date: 2025-06-10 14:41:29.391395
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = 'cae942c398cb'
|
||||||
|
down_revision: Union[str, None] = None
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('deans',
|
||||||
|
sa.Column('email', sa.String(length=50), nullable=False),
|
||||||
|
sa.Column('password', sa.String(length=256), nullable=False),
|
||||||
|
sa.Column('name', sa.String(length=50), nullable=False),
|
||||||
|
sa.Column('surname', sa.String(length=50), nullable=False),
|
||||||
|
sa.Column('patronymic', sa.String(length=50), nullable=True),
|
||||||
|
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('email')
|
||||||
|
)
|
||||||
|
op.create_table('specializations',
|
||||||
|
sa.Column('name', sa.String(length=256), nullable=False),
|
||||||
|
sa.Column('code', sa.String(length=50), nullable=False),
|
||||||
|
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('code'),
|
||||||
|
sa.UniqueConstraint('name')
|
||||||
|
)
|
||||||
|
op.create_table('facults',
|
||||||
|
sa.Column('name', sa.String(length=256), nullable=False),
|
||||||
|
sa.Column('dean_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['dean_id'], ['deans.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('name')
|
||||||
|
)
|
||||||
|
op.create_table('groups',
|
||||||
|
sa.Column('course', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('specialization_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['specialization_id'], ['specializations.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('students',
|
||||||
|
sa.Column('specialization_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('status', sa.Enum('STUDY', 'ACADEM', 'EXPLUSION', 'GRADUATED', name='studentstatus'), nullable=False),
|
||||||
|
sa.Column('group_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(length=50), nullable=False),
|
||||||
|
sa.Column('surname', sa.String(length=50), nullable=False),
|
||||||
|
sa.Column('patronymic', sa.String(length=50), nullable=True),
|
||||||
|
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['group_id'], ['groups.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['specialization_id'], ['specializations.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('students')
|
||||||
|
op.drop_table('groups')
|
||||||
|
op.drop_table('facults')
|
||||||
|
op.drop_table('specializations')
|
||||||
|
op.drop_table('deans')
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
"""Initial
|
|
||||||
|
|
||||||
Revision ID: faa64c3fcd68
|
|
||||||
Revises:
|
|
||||||
Create Date: 2025-06-09 22:57:38.958126
|
|
||||||
|
|
||||||
"""
|
|
||||||
from typing import Sequence, Union
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision: str = 'faa64c3fcd68'
|
|
||||||
down_revision: Union[str, None] = None
|
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.create_table('users',
|
|
||||||
sa.Column('first_name', sa.String(length=50), nullable=False),
|
|
||||||
sa.Column('last_name', sa.String(length=50), nullable=False),
|
|
||||||
sa.Column('email', sa.String(length=50), nullable=False),
|
|
||||||
sa.Column('password', sa.String(length=256), nullable=False),
|
|
||||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
|
||||||
sa.Column('created_at', sa.TIMESTAMP(), server_default=sa.text('now()'), nullable=False),
|
|
||||||
sa.Column('updated_at', sa.TIMESTAMP(), server_default=sa.text('now()'), nullable=False),
|
|
||||||
sa.PrimaryKeyConstraint('id'),
|
|
||||||
sa.UniqueConstraint('email')
|
|
||||||
)
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.drop_table('users')
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
5
backend/app/models/__init__.py
Normal file
5
backend/app/models/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from .student import Student
|
||||||
|
from .group import Group
|
||||||
|
from .specialization import Specialization
|
||||||
|
from .dean import Dean
|
||||||
|
from .facult import Facult
|
||||||
13
backend/app/models/dean.py
Normal file
13
backend/app/models/dean.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from sqlalchemy.orm import Mapped, relationship, mapped_column
|
||||||
|
from sqlalchemy import String
|
||||||
|
|
||||||
|
|
||||||
|
from app.dao.database import Base
|
||||||
|
from app.models.facult import Facult
|
||||||
|
from .mixins import HumanMixin
|
||||||
|
|
||||||
|
|
||||||
|
class Dean(HumanMixin, Base):
|
||||||
|
facult: Mapped[Facult | None] = relationship(back_populates="dean")
|
||||||
|
email: Mapped[str] = mapped_column(String(50), unique=True, nullable=False)
|
||||||
|
password: Mapped[str] = mapped_column(String(256), nullable=False)
|
||||||
10
backend/app/models/facult.py
Normal file
10
backend/app/models/facult.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from sqlalchemy import ForeignKey, String
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
|
|
||||||
|
from app.dao.database import Base
|
||||||
|
|
||||||
|
|
||||||
|
class Facult(Base):
|
||||||
|
name: Mapped[str] = mapped_column(String(256), nullable=False, unique=True)
|
||||||
|
dean_id: Mapped[int | None] = mapped_column(ForeignKey("deans.id"), nullable=True)
|
||||||
|
dean: Mapped["Dean"] = relationship("Dean", back_populates="facult", lazy="joined")
|
||||||
12
backend/app/models/group.py
Normal file
12
backend/app/models/group.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from sqlalchemy import ForeignKey, Integer
|
||||||
|
from app.dao.database import Base
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
|
|
||||||
|
|
||||||
|
class Group(Base):
|
||||||
|
course: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
|
students: Mapped[list["Student"]] = relationship(back_populates="group")
|
||||||
|
specialization_id: Mapped[int] = mapped_column(ForeignKey("specializations.id"))
|
||||||
|
specialization: Mapped["Specialization"] = relationship(
|
||||||
|
"Specialization", back_populates="groups", lazy="joined"
|
||||||
|
)
|
||||||
10
backend/app/models/mixins.py
Normal file
10
backend/app/models/mixins.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from sqlalchemy import String
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
|
|
||||||
|
|
||||||
|
class HumanMixin:
|
||||||
|
name: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||||
|
surname: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||||
|
patronymic: Mapped[str | None] = mapped_column(
|
||||||
|
String(50), nullable=True, default=None
|
||||||
|
)
|
||||||
10
backend/app/models/specialization.py
Normal file
10
backend/app/models/specialization.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from sqlalchemy import String
|
||||||
|
from app.dao.database import Base
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
|
|
||||||
|
|
||||||
|
class Specialization(Base):
|
||||||
|
name: Mapped[str] = mapped_column(String(256), nullable=False, unique=True)
|
||||||
|
code: Mapped[str] = mapped_column(String(50), nullable=False, unique=True)
|
||||||
|
students: Mapped[list["Student"]] = relationship(back_populates="specialization")
|
||||||
|
groups: Mapped[list["Group"]] = relationship(back_populates="specialization")
|
||||||
27
backend/app/models/student.py
Normal file
27
backend/app/models/student.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import enum
|
||||||
|
from sqlalchemy import ForeignKey, Enum
|
||||||
|
|
||||||
|
from app.dao.database import Base
|
||||||
|
from .mixins import HumanMixin
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
|
|
||||||
|
|
||||||
|
class StudentStatus(enum.Enum):
|
||||||
|
STUDY = "Обучается"
|
||||||
|
ACADEM = "Академ"
|
||||||
|
EXPLUSION = "Отчислен"
|
||||||
|
GRADUATED = "Выпущен"
|
||||||
|
|
||||||
|
|
||||||
|
class Student(HumanMixin, Base):
|
||||||
|
specialization_id: Mapped[int] = mapped_column(ForeignKey("specializations.id"))
|
||||||
|
specialization: Mapped["Specialization"] = relationship(
|
||||||
|
"Specialization", back_populates="groups", lazy="joined"
|
||||||
|
)
|
||||||
|
status: Mapped[StudentStatus] = mapped_column(
|
||||||
|
Enum(StudentStatus), nullable=False, default=StudentStatus.STUDY
|
||||||
|
)
|
||||||
|
group_id: Mapped[int] = mapped_column(ForeignKey("groups.id"))
|
||||||
|
group: Mapped["Group"] = relationship(
|
||||||
|
"Group", back_populates="students", lazy="joined"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user