Conversation
IAmLamarr
left a comment
There was a problem hiding this comment.
Общие замечания
- Нет линтеров / форматтеров => местами нарушается код-стайл
Рекомендую поставить и сконфигурировать ruff - Нет README.md, хотелось бы видеть базовое описание проекта, как его запускать
- Нет .env-example, непонятно какие переменные среды нужно заполнить чтобы стартовать проект, нужно ознакамливаться со всей кодовой базой перед запуском
- Не зафиксирована версия python, на которой работает проект
Можно зафиксировать в README.md или использовать poetry или uv для менеджмента зависимостей и версий python - Нет Dockerfile
- "Слишком умные" роутеры, не реализовано разделение "сервис - репозиторий", из-за этого сильно дублируется код, связанный с получением тех или иных сущностей
- Некорректная работа с роутерами
Нужно выделять общие роуты через префиксы, это уменьшит количество ошибок при создании URL + даст возможность реализовывать свои dependencies в роутах, уменьшать дублирование логики - Магические числа в части работы с ролями
Если хочется проверить наличие роли в рамках команды, нужно завязываться на name роли (а еще лучше сделать отдельный dependency для этой задачи) - Коды ответов HTTP проще указывать цифрой, а не импортировать из fastapi
Коды везде одинаковые, не вижу смысла в импорте; Это уменьшит количество лишнего кода - Сложно читать из-за импортов
Нужно стараться использовать конструкцию from ... import ...
Пример:
import typing
import project_schemas
import user
import fastapi
import orm
import team
import project
...
@router.get("/api/team/{team_id}/projects", response_model=typing.List[project_schemas.ProjectResponse],
status_code=fastapi.status.HTTP_200_OK)
def get_team_projects(team_id: int, current_user: user.User = fastapi.Depends(auth.get_current_user),
data_base: orm.Session = fastapi.Depends(db.get_db)):
"""Получить все проекты в команде team_id"""
team_obj = data_base.query(team.Team).filter(team.Team.id == team_id).first()
if not team_obj:
raise fastapi.HTTPException(status_code=fastapi.status.HTTP_404_NOT_FOUND, detail="Команда не найдена")
user_team = data_base.query(team.UserTeam).filter(team.UserTeam.user_id == current_user.id,
team.UserTeam.team_id == team_id).first()
if not user_team:
raise fastapi.HTTPException(status_code=fastapi.status.HTTP_403_FORBIDDEN,
detail="У вас нет доступа к этой команде")
projects = data_base.query(project.Project).filter(project.Project.team_id == team_id).all()
return projectsИспользуем from import:
from typing import List
from project_schemas import ProjectResponse
from user import User
from fastapi import Depends, HTTPException
from orm import Session
from team import Team, UserTeam
from project import Project
...
@router.get("/api/team/{team_id}/projects", response_model=List[ProjectResponse],
status_code=200)
def get_team_projects(team_id: int, current_user: User = Depends(auth.get_current_user),
data_base: Session = Depends(db.get_db)):
"""Получить все проекты в команде team_id"""
team_obj = data_base.query(Team).filter(Team.id == team_id).first()
if not team_obj:
raise HTTPException(status_code=404, detail="Команда не найдена")
user_team = data_base.query(UserTeam).filter(UserTeam.user_id == current_user.id,
UserTeam.team_id == team_id).first()
if not user_team:
raise HTTPException(status_code=403,
detail="У вас нет доступа к этой команде")
projects = data_base.query(Project).filter(Project.team_id == team_id).all()
return projectsВторой вариант читается гораздо легче, т.к модули не упоминаются множество раз в коде
- Подумать, точно ли нужны точечные сообщения для каждой ошибки или будет достаточно общих
Можно ли отдавать в get_team_projects в случае 403 сообщение "Недостаточно прав" вместо "У вас нет доступа к этой команде"
Если да, то можно вынести сделать реестр общих ошибок и выдавать их оттуда, вместо того, чтобы каждый раз писать логику для каждого роута
There was a problem hiding this comment.
Неполный gitignore, может не подойти для другой IDE
Игноры можно брать отсюда, в данном случае нужен игнор для "Python"
| from app.core import db, security | ||
| from app.models import user | ||
|
|
||
| router = fastapi.APIRouter() |
There was a problem hiding this comment.
Указать тег auth?
| try: | ||
| data_base.commit() | ||
| data_base.refresh(u) | ||
| except exc.IntegrityError: | ||
| data_base.rollback() | ||
| raise fastapi.HTTPException(status_code=fastapi.status.HTTP_409_CONFLICT, | ||
| detail="Пользователь с таким email или именем уже существует") |
There was a problem hiding this comment.
Не совсем корректно обрабатывать так проверку уникальности email и nickname
Если в будущем добавится новое поле с unique и нарушится его уникальность, вернется ошибка с тем что, что-то не то с email/nickname
Лучше запросить юзера из БД по такому email или такому nickname и если что-то нашлось - выдавать ошибку
Если не нашлось - добавлять нового юзера
| if not stream_obj: | ||
| raise fastapi.HTTPException(status_code=fastapi.status.HTTP_404_NOT_FOUND, detail="Стрим не найден") | ||
|
|
||
| project_obj = data_base.query(project.Project).filter(project.Project.id == stream_obj.project_id).first() |
There was a problem hiding this comment.
Нет проверки project_obj по аналогии с stream_obj и user_team
| if not stream_obj: | ||
| raise fastapi.HTTPException(status_code=fastapi.status.HTTP_404_NOT_FOUND, detail="Стрим не найден") | ||
|
|
||
| project_obj = data_base.query(project.Project).filter(project.Project.id == stream_obj.project_id).first() |
There was a problem hiding this comment.
Нет проверки project_obj
|
|
||
| Base = declarative_base() No newline at end of file |
There was a problem hiding this comment.
Можно вынести id в базовую модель
| class UserTask(base.Base): | ||
| __tablename__ = "UserTask" | ||
| id = Column(Integer, primary_key=True) | ||
| user_id = Column(Integer, ForeignKey("Users.id"), nullable=False) | ||
| task_id = Column(Integer, ForeignKey("Tasks.id"), nullable=False) | ||
|
|
||
| user = relationship("User", backref="user_tasks") | ||
| task = relationship("Task", backref="user_tasks") |
There was a problem hiding this comment.
Кажется, это должно быть в models/task.py
| class UserRole(str, Enum): | ||
| READER = "Reader" | ||
| EDITOR = "Editor" |
There was a problem hiding this comment.
Где-то выше уже есть такой enum
| @field_validator("email") | ||
| def validate_email(cls, value): | ||
| if not value: | ||
| raise fastapi.HTTPException(status_code=400, detail="Некорректный формат email") | ||
| return value |
There was a problem hiding this comment.
А что не так со стандартной валидацией?
| @pytest.fixture(scope="session", autouse=True) | ||
| def load_env_and_models(): | ||
| env_path = Path(__file__).parent.parent.parent / '.env' | ||
| load_dotenv(env_path) | ||
|
|
||
| import app.models.goal | ||
| import app.models.task | ||
| import app.models.meta No newline at end of file |
There was a problem hiding this comment.
Почему происходит импорт внутри функции? Это плохая практика
В load_dotenv кажется можно не подавать такой путь, он по умолчанию будет искать .env в той директории, откуда запускается проект
No description provided.