Skip to content

Commit 817066f

Browse files
authored
Merge pull request #7 from dmsnback/feature/users
Пользователи: Модель, Схема, Роутеры, crud
2 parents 08559e4 + b493c1d commit 817066f

10 files changed

Lines changed: 258 additions & 4 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""Users Migration
2+
3+
Revision ID: f210b1391ab0
4+
Revises: ffb4e984a7e9
5+
Create Date: 2026-02-25 12:37:10.316531
6+
7+
"""
8+
from typing import Sequence, Union
9+
10+
from alembic import op
11+
import sqlalchemy as sa
12+
13+
14+
# revision identifiers, used by Alembic.
15+
revision: str = 'f210b1391ab0'
16+
down_revision: Union[str, Sequence[str], None] = 'ffb4e984a7e9'
17+
branch_labels: Union[str, Sequence[str], None] = None
18+
depends_on: Union[str, Sequence[str], None] = None
19+
20+
21+
def upgrade() -> None:
22+
"""Upgrade schema."""
23+
# ### commands auto generated by Alembic - please adjust! ###
24+
op.create_table('users',
25+
sa.Column('id', sa.Integer(), nullable=False),
26+
sa.Column('telegram_id', sa.Integer(), nullable=False),
27+
sa.Column('telegram_username', sa.String(length=256), nullable=True),
28+
sa.Column('create_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
29+
sa.PrimaryKeyConstraint('id'),
30+
sa.UniqueConstraint('telegram_id')
31+
)
32+
# ### end Alembic commands ###
33+
34+
35+
def downgrade() -> None:
36+
"""Downgrade schema."""
37+
# ### commands auto generated by Alembic - please adjust! ###
38+
op.drop_table('users')
39+
# ### end Alembic commands ###

app/core/base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Импорты класса Base и всех моделей Alembic."""
22

33
from app.core.database import Base # noqa
4+
from app.models.users import User # noqa
45
from app.models.words import Word # noqa

app/crud/users.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import logging
2+
3+
from sqlalchemy import select
4+
from sqlalchemy.exc import SQLAlchemyError
5+
from sqlalchemy.ext.asyncio import AsyncSession
6+
7+
from app.models.users import User
8+
from app.schemas.users import UserCreateSchema
9+
10+
logger = logging.getLogger(__name__)
11+
12+
13+
class CRUDUser:
14+
15+
async def create_user(self, user: UserCreateSchema, session: AsyncSession):
16+
try:
17+
new_user = User(**user.model_dump())
18+
session.add(new_user)
19+
await session.flush()
20+
await session.commit()
21+
await session.refresh(new_user)
22+
logger.info(
23+
f"Добавлен пользователь '{new_user.telegram_username}' id: {new_user.telegram_id} "
24+
)
25+
return new_user
26+
except SQLAlchemyError as e:
27+
logger.error(f"Ошибка при добавлении пользователя: {e}")
28+
raise
29+
30+
async def get_all_users(self, session: AsyncSession):
31+
try:
32+
query = select(User)
33+
result = await session.execute(query)
34+
users = result.scalars().all()
35+
logger.info("Получен список всех пользователей")
36+
return users
37+
except SQLAlchemyError as e:
38+
logger.error(
39+
f"Ошибка при получении списка всех пользователей: {e}"
40+
)
41+
raise
42+
43+
async def get_user(self, telegram_id: int, session: AsyncSession):
44+
try:
45+
query = select(User).where(User.telegram_id == telegram_id)
46+
result = await session.execute(query)
47+
user = result.scalars().first()
48+
if user:
49+
logger.info(
50+
f"Получен пользователь: '{user.telegram_username}' id={user.telegram_id}"
51+
)
52+
return user
53+
else:
54+
logger.warning(
55+
f"Пользователь id={telegram_id} не найден в базе"
56+
)
57+
raise
58+
except SQLAlchemyError as e:
59+
logger.error(
60+
f"Ошибка при получении пользователя id={telegram_id}: {e}"
61+
)
62+
raise
63+
64+
async def delete_user(self, user: User, session: AsyncSession):
65+
try:
66+
await session.delete(user)
67+
await session.commit()
68+
logger.info(
69+
f"Пользователь: '{user.telegram_username}' id={user.telegram_id} удалён"
70+
)
71+
except SQLAlchemyError as e:
72+
logger.error(
73+
f"Ошибка при удалении пользователя '{user.telegram_username}' id={user.telegram_id}: {e}"
74+
)
75+
raise

app/crud/words.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ async def get_word(self, word: str, session: AsyncSession):
4444
result = await session.execute(query)
4545
find_word = result.scalars().first()
4646
if find_word:
47-
logger.info(f"Получено слово: '{find_word}'")
47+
logger.info(
48+
f"Получено слово: '{find_word.english} - {find_word.russian}'"
49+
)
4850
else:
4951
logger.warning(f"Слово '{word}' не найдено в базе")
5052
return find_word

app/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from app.core.config import settings
77
from app.core.logging import setup_logging
88
from app.routers.questions import question_router
9+
from app.routers.users import user_router
910
from app.routers.words import word_router
1011

1112
setup_logging()
@@ -28,3 +29,4 @@ async def lifespan(app: FastAPI):
2829

2930
app.include_router(word_router)
3031
app.include_router(question_router)
32+
app.include_router(user_router)

app/models/users.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from datetime import datetime
2+
3+
from sqlalchemy import DateTime, Integer, String, func
4+
from sqlalchemy.orm import Mapped, mapped_column
5+
6+
from app.core.database import Base
7+
8+
9+
class User(Base):
10+
__tablename__ = "users"
11+
12+
id: Mapped[int] = mapped_column(Integer, primary_key=True)
13+
telegram_id: Mapped[int] = mapped_column(
14+
Integer, nullable=False, unique=True
15+
)
16+
telegram_username: Mapped[str] = mapped_column(String(256), nullable=True)
17+
create_at: Mapped[datetime] = mapped_column(
18+
DateTime(timezone=True), server_default=func.now(), nullable=False
19+
)

app/routers/questions.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from app.core.database import get_session
77
from app.crud.words import CRUDWord
8-
from app.schemas.words import QuestionResponseSchema
8+
from app.schemas.questions import QuestionReadSchema
99

1010
question_router = APIRouter(
1111
prefix="/questions",
@@ -18,11 +18,11 @@
1818

1919

2020
@question_router.get(
21-
"", response_model=QuestionResponseSchema, summary="Получение вопроса"
21+
"", response_model=QuestionReadSchema, summary="Получение вопроса"
2222
)
2323
async def get_question(
2424
session: AsyncSession = Depends(get_session),
25-
) -> QuestionResponseSchema:
25+
) -> QuestionReadSchema:
2626
words = await word_crud.get_all_words(session)
2727
if len(words) < 4:
2828
return {"error": "В базее должно быть минимум 4 слова"}

app/routers/users.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from fastapi import APIRouter, Depends, HTTPException, status
2+
from sqlalchemy.ext.asyncio import AsyncSession
3+
4+
from app.core.database import get_session
5+
from app.crud.users import CRUDUser
6+
from app.schemas.users import UserCreateSchema, UserReadSchema
7+
8+
user_router = APIRouter(
9+
prefix="/users",
10+
tags=[
11+
"Пользователи",
12+
],
13+
)
14+
15+
user_crud = CRUDUser()
16+
17+
18+
@user_router.post(
19+
"", response_model=UserReadSchema, summary="Добавление пользователя"
20+
)
21+
async def add_user(
22+
user: UserCreateSchema, session: AsyncSession = Depends(get_session)
23+
) -> UserReadSchema:
24+
try:
25+
add_user = await user_crud.create_user(user, session)
26+
return add_user
27+
except Exception as e:
28+
raise e
29+
30+
31+
@user_router.delete("/{telegram_id}", summary="Удаление Пользователя")
32+
async def delete_user(
33+
telegram_id: int, session: AsyncSession = Depends(get_session)
34+
) -> dict:
35+
user = await user_crud.get_user(telegram_id, session)
36+
if not user:
37+
raise HTTPException(
38+
status_code=status.HTTP_404_NOT_FOUND,
39+
detail="Пользователь не найден",
40+
)
41+
try:
42+
await user_crud.delete_user(user, session)
43+
return {"detail": f"Пользователь id={telegram_id} удален"}
44+
except Exception:
45+
raise HTTPException(
46+
status.HTTP_500_INTERNAL_SERVER_ERROR,
47+
detail="Ошибка при удалении пользователя",
48+
)
49+
50+
51+
@user_router.get(
52+
"/all",
53+
response_model=list[UserReadSchema],
54+
summary="Получение всех пользователей",
55+
)
56+
async def get_all_users(
57+
session: AsyncSession = Depends(get_session),
58+
) -> list[UserReadSchema]:
59+
try:
60+
users = await user_crud.get_all_users(session)
61+
if not users:
62+
raise HTTPException(
63+
status_code=status.HTT_404_NOT_FOUND,
64+
detail="Список пользоввателей отсутсттвует",
65+
)
66+
return users
67+
except Exception as e:
68+
raise e
69+
70+
71+
@user_router.get(
72+
"/{telegram_id}",
73+
response_model=UserReadSchema,
74+
summary="Получение пользователя по telegram_id",
75+
)
76+
async def get_user(
77+
telegram_id: int, session: AsyncSession = Depends(get_session)
78+
) -> UserReadSchema:
79+
try:
80+
user = await user_crud.get_user(telegram_id, session)
81+
if not user:
82+
raise HTTPException(
83+
status_code=status.HTTP_404_NOT_FOUND,
84+
detail=f"Пользователь id={telegram_id} не найден",
85+
)
86+
return user
87+
except Exception as e:
88+
raise e

app/schemas/questions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from pydantic import BaseModel
2+
3+
4+
class QuestionReadSchema(BaseModel):
5+
word: str
6+
options: list[str]
7+
correct: str
8+
direction: str

app/schemas/users.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from datetime import datetime
2+
3+
from pydantic import BaseModel
4+
5+
6+
class UserBaseSchema(BaseModel):
7+
telegram_id: int
8+
telegram_username: str | None = None
9+
10+
11+
class UserCreateSchema(UserBaseSchema):
12+
pass
13+
14+
15+
class UserReadSchema(UserBaseSchema):
16+
id: int
17+
create_at: datetime
18+
19+
class Config:
20+
orm_mode = True

0 commit comments

Comments
 (0)