Код ревью - #1
samsonovroma2011-cmd wants to merge 1 commit into
Conversation
| from sqlalchemy.orm import as_declarative | ||
|
|
||
|
|
||
| @as_declarative() |
There was a problem hiding this comment.
не знакомый декоратор
There was a problem hiding this comment.
Советую изучить, что он делает и для чего
| def init_handlers(app_: FastAPI) -> None: | ||
|
|
||
| @app_.exception_handler(CustomException) | ||
| async def custom_exception_handler(request: Request, exc: CustomException): | ||
| return JSONResponse( | ||
| status_code=exc.status_code, | ||
| content={ | ||
| "status": exc.status, | ||
| "status_type": exc.status_type, | ||
| "message": exc.message | ||
| }, | ||
| ) | ||
|
|
||
| @app_.exception_handler(RequestValidationError) | ||
| async def validation_exception_handler(request: Request, exc: RequestValidationError): | ||
| reformatted_message = defaultdict(list) | ||
| field_string = '' | ||
| msg = '' | ||
|
|
||
| for pydantic_error in exc.errors(): | ||
| loc, msg, error_type = pydantic_error["loc"], pydantic_error["msg"], pydantic_error["type"] | ||
|
|
||
| if error_type == "value_error.jsondecode": | ||
| return await custom_exception_handler(request=request, exc=BadRequestException()) | ||
|
|
||
| filtered_loc = loc[1:] if loc[0] in ("body", "query", "path") and loc[1:] else loc | ||
| field_string = ".".join(filtered_loc) | ||
| reformatted_message[field_string].append(msg) | ||
|
|
||
| return JSONResponse( | ||
| status_code=UnprocessableEntity().status_code, | ||
| content={ | ||
| "status": UnprocessableEntity.status, | ||
| "status_type": UnprocessableEntity.status_type, | ||
| "message": field_string.capitalize() + ' ' + msg, | ||
| "errors": reformatted_message | ||
| } | ||
| ) |
There was a problem hiding this comment.
Слишком нагруженная функция
There was a problem hiding this comment.
Как бы сделал по другому. Интересно решение
| def __getattr__(self, name): | ||
| return getattr(self._session, name) | ||
|
|
||
| async def init(self): |
There was a problem hiding this comment.
Хорошо что используется асинхронность
| loguru = "^0.6.0" | ||
| cryptography = "^38.0.1" | ||
| bcrypt = "^4.0.0" | ||
| sqlalchemy = "^1.4.46" |
There was a problem hiding this comment.
Используется старая версия sqlalchemy
samsonovroma2011-cmd
left a comment
There was a problem hiding this comment.
Мне показалось что в целом у проекта хорошая архитектура. Могу выделить в ней несколько плюсов: используется паттерн репозиторий, есть сервисный слой и виден очень сильный ООП подход.
Я понял этот проект на 50-60%.
| class Settings(BaseSettings): | ||
| ENV: str = "dev" | ||
| PROJECT_NAME: str = "Boilerplate" | ||
| PROJECT_TITLE: str = "FastAPI Boilerplate" | ||
| PROJECT_DESCRIPTION: str = "FastAPI Boilerplate" | ||
| PROJECT_VERSION: str = "1.0.0" | ||
|
|
||
| ENCODING: str = 'utf-8' | ||
|
|
||
| API_V1_PREFIX: str = "/api/v1" | ||
| JWT_ALGORITHM: str = "HS256" | ||
| JWT_ACCESS_SECRET_KEY: str = "MySuperSecret" | ||
| JWT_REFRESH_SECRET_KEY: str = "MySuperSecretRefresh" |
There was a problem hiding this comment.
Секретные данные должны быть в в файле .env и от туда их должен брать класс с настройками
There was a problem hiding this comment.
Да. Тут и нету секретных данных. Это шаблоны заглушки
|
|
||
| from sqlalchemy import Column, Integer, String, ForeignKey, Table, PrimaryKeyConstraint | ||
| from sqlalchemy.orm import declarative_base, relationship | ||
|
|
||
| from core.db.base import Base | ||
|
|
||
|
|
||
| user_roles_table = Table( | ||
| "user_roles", | ||
| Base.metadata, | ||
| Column("user_id", ForeignKey("users.id")), | ||
| Column("role_id", ForeignKey("roles.id")), | ||
| PrimaryKeyConstraint('user_id', 'role_id'), | ||
| ) | ||
|
|
||
|
|
||
| class RoleModel(Base): | ||
| def __init__(self): | ||
| pass | ||
|
|
||
| __tablename__ = "roles" | ||
|
|
||
| id: int = Column(Integer, primary_key=True, autoincrement=True) | ||
| role: str = Column(String(255), nullable=False) | ||
| __mapper_args__ = {"eager_defaults": True} | ||
|
|
||
| users = relationship( | ||
| "UserModel", secondary=user_roles_table, back_populates="roles" | ||
| ) | ||
|
|
||
|
|
||
| class UserModel(Base): | ||
| def __init__(self): | ||
| pass | ||
|
|
||
| __tablename__ = "users" | ||
|
|
||
| id: int = Column(Integer, primary_key=True, autoincrement=True) | ||
| name: str = Column(String(255), nullable=False) | ||
| email: str = Column(String(255), nullable=False, unique=True) | ||
| password: str = Column(String(255), nullable=False) | ||
| phone: str = Column(String(255), nullable=False) | ||
| __mapper_args__ = {"eager_defaults": True} | ||
|
|
||
| roles: List[RoleModel] = relationship( | ||
| "RoleModel", | ||
| secondary=user_roles_table, | ||
| back_populates="users", | ||
| ) |
There was a problem hiding this comment.
Смешивание подходов создание моделей. Декларативный и императивный подходы смешаны
| await self.db.rollback() | ||
|
|
||
| user_dict = user.dict() | ||
| role_name = user.role | ||
| del user_dict["role"] |
There was a problem hiding this comment.
Для чего rollback до выполнение операции? Почему del? Советую изучить
| def init_handlers(app_: FastAPI) -> None: | ||
|
|
||
| @app_.exception_handler(CustomException) | ||
| async def custom_exception_handler(request: Request, exc: CustomException): | ||
| return JSONResponse( | ||
| status_code=exc.status_code, | ||
| content={ | ||
| "status": exc.status, | ||
| "status_type": exc.status_type, | ||
| "message": exc.message | ||
| }, | ||
| ) | ||
|
|
||
| @app_.exception_handler(RequestValidationError) | ||
| async def validation_exception_handler(request: Request, exc: RequestValidationError): | ||
| reformatted_message = defaultdict(list) | ||
| field_string = '' | ||
| msg = '' | ||
|
|
||
| for pydantic_error in exc.errors(): | ||
| loc, msg, error_type = pydantic_error["loc"], pydantic_error["msg"], pydantic_error["type"] | ||
|
|
||
| if error_type == "value_error.jsondecode": | ||
| return await custom_exception_handler(request=request, exc=BadRequestException()) | ||
|
|
||
| filtered_loc = loc[1:] if loc[0] in ("body", "query", "path") and loc[1:] else loc | ||
| field_string = ".".join(filtered_loc) | ||
| reformatted_message[field_string].append(msg) | ||
|
|
||
| return JSONResponse( | ||
| status_code=UnprocessableEntity().status_code, | ||
| content={ | ||
| "status": UnprocessableEntity.status, | ||
| "status_type": UnprocessableEntity.status_type, | ||
| "message": field_string.capitalize() + ' ' + msg, | ||
| "errors": reformatted_message | ||
| } | ||
| ) |
There was a problem hiding this comment.
Как бы сделал по другому. Интересно решение
| class Settings(BaseSettings): | ||
| ENV: str = "dev" | ||
| PROJECT_NAME: str = "Boilerplate" | ||
| PROJECT_TITLE: str = "FastAPI Boilerplate" | ||
| PROJECT_DESCRIPTION: str = "FastAPI Boilerplate" | ||
| PROJECT_VERSION: str = "1.0.0" | ||
|
|
||
| ENCODING: str = 'utf-8' | ||
|
|
||
| API_V1_PREFIX: str = "/api/v1" | ||
| JWT_ALGORITHM: str = "HS256" | ||
| JWT_ACCESS_SECRET_KEY: str = "MySuperSecret" | ||
| JWT_REFRESH_SECRET_KEY: str = "MySuperSecretRefresh" |
There was a problem hiding this comment.
Да. Тут и нету секретных данных. Это шаблоны заглушки
| from sqlalchemy.orm import as_declarative | ||
|
|
||
|
|
||
| @as_declarative() |
There was a problem hiding this comment.
Советую изучить, что он делает и для чего
Я проведу код ревью проекта https://github.com/theshohidul/FastAPI-Boilerplate