Итоговые рекомендации, чек-листы, антипаттерны
Итоговые рекомендации, чек-листы, антипаттерны.
project/
├── app/
│ ├── __init__.py
│ ├── main.py # Создание приложения
│ ├── config.py # Настройки
│ ├── dependencies.py # Зависимости
│ ├── exceptions.py # Исключения
│ ├── api/
│ │ ├── __init__.py
│ │ ├── v1/
│ │ │ ├── __init__.py
│ │ │ ├── users.py
│ │ │ └── posts.py
│ │ └── v2/
│ ├── models/ # SQLAlchemy модели
│ ├── schemas/ # Pydantic схемы
│ ├── services/ # Бизнес-логика
│ ├── repositories/ # Доступ к БД
│ └── utils/ # Утилиты
├── tests/
├── alembic/
├── Dockerfile
├── docker-compose.yml
├── pyproject.toml
└── .env.example
# Плохо
def get_user(id):
return db.query(User).get(id)
# Хорошо
def get_user(user_id: int) -> User | None:
return db.query(User).filter(User.id == user_id).first()# Плохо
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# Хорошо
from typing import Generator
def get_db() -> Generator[Session, None, None]:
db = SessionLocal()
try:
yield db
finally:
db.close()from typing import Annotated
# Хорошо (Python 3.10+)
def get_user(
user_id: Annotated[int, Path(..., ge=1)],
db: Annotated[Session, Depends(get_db)]
):
...# Плохо
logger.info(f"User password: {user.password}")
# Хорошо
logger.info(f"User {user.username} logged in")# Плохо
@app.post('/users')
def create_user(email: str):
# Нет валидации email
...
# Хорошо
@app.post('/users')
def create_user(user: UserCreate):
# Pydantic валидирует email
...# Плохо (SQL injection)
query = f"SELECT * FROM users WHERE email = '{email}'"
# Хорошо
user = db.query(User).filter(User.email == email).first()# Плохо (блокирует)
@app.get('/external')
def get_external():
response = requests.get('https://api.example.com')
return response.json()
# Хорошо
@app.get('/external')
async def get_external():
async with httpx.AsyncClient() as client:
response = await client.get('https://api.example.com')
return response.json()from functools import lru_cache
@lru_cache(maxsize=128)
def get_config() -> dict:
return load_config_from_file()# Плохо
@app.get('/items')
def get_items():
return db.query(Item).all() # Может быть огромным
# Хорошо
@app.get('/items')
def get_items(skip: int = 0, limit: int = 100):
return db.query(Item).offset(skip).limit(limit).all()def test_create_user(client):
response = client.post('/users', json={
'username': 'test',
'email': 'test@example.com',
'password': 'testpass123'
})
assert response.status_code == 201
assert 'id' in response.json()@pytest.fixture
def test_user(db):
user = User(username='test', email='test@example.com')
db.add(user)
db.commit()
return user
def test_get_user(client, test_user):
response = client.get(f'/users/{test_user.id}')
assert response.status_code == 200from unittest.mock import patch
@patch('app.services.external_api.fetch_data')
def test_with_mock(mock_fetch):
mock_fetch.return_value = {'data': 'mocked'}
response = client.get('/external')
assert response.json() == {'data': 'mocked'}@app.get(
'/users',
summary="Получить список пользователей",
description="Возвращает пагинированный список пользователей",
response_description="Список пользователей"
)
def get_users(...):
"""
Получает пользователей с поддержкой пагинации.
## Пример ответа
```json
{"users": [...], "total": 100}
```
"""
...class UserCreate(BaseModel):
username: str = Field(..., example="john_doe")
email: EmailStr = Field(..., example="john@example.com")@app.get('/health')
def health_check():
return {'status': 'healthy'}@app.middleware("http")
async def log_requests(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
duration = time.time() - start_time
logger.info(f"{request.method} {request.url.path} - {duration:.3f}s")
return responsefrom prometheus_fastapi_instrumentator import Instrumentator
@app.on_event("startup")
async def startup():
Instrumentator().instrument(app).expose(app)# Плохо
@app.post('/orders')
def create_order(order: OrderCreate):
# Валидация, расчёты, отправка email — всё здесь
...
# Хорошо
@app.post('/orders')
def create_order(order: OrderCreate, service: OrderService = Depends()):
return service.create_order(order)# Плохо
@app.get('/users/{id}')
def get_user(id: int, db: Session = Depends(get_db)):
return db.query(User).get(id)
# Хорошо
@app.get('/users/{id}')
def get_user(id: int, repo: UserRepository = Depends()):
return repo.get_by_id(id)# Плохо
try:
send_email()
except:
pass # Игнорирование ошибки
# Хорошо
try:
send_email()
except SMTPError as e:
logger.error(f"Failed to send email: {e}")
raise# Плохо
@app.get('/slow')
async def slow():
time.sleep(5) # Блокирует event loop
return {'done': True}
# Хорошо
@app.get('/slow')
async def slow():
await asyncio.sleep(5)
return {'done': True}Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.