← Назад к вопросам
Какой уровень запросов делал в SQLAlchemy?
2.0 Middle🔥 121 комментариев
#Базы данных (SQL)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
SQLAlchemy: уровни запросов
В SQLAlchemy есть три основных уровня работы с БД: Core, ORM и выполнение сырого SQL. Я использую все три в зависимости от задачи.
1. SQLAlchemy ORM (Object-Relational Mapping)
Самый высокоуровневый подход. Работаю с объектами Python вместо SQL.
Определение модели:
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
from sqlalchemy.orm import declarative_base, relationship
from datetime import datetime
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String(50), unique=True, nullable=False)
email = Column(String(100), nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
posts = relationship('Post', back_populates='author')
class Post(Base):
__tablename__ = 'posts'
id = Column(Integer, primary_key=True)
title = Column(String(200), nullable=False)
content = Column(String, nullable=False)
user_id = Column(Integer, ForeignKey('users.id'))
created_at = Column(DateTime, default=datetime.utcnow)
author = relationship('User', back_populates='posts')
Использование ORM:
from sqlalchemy.orm import Session
def create_user(db: Session, username: str, email: str) -> User:
user = User(username=username, email=email)
db.add(user)
db.commit()
db.refresh(user)
return user
def get_user_by_id(db: Session, user_id: int) -> User:
return db.query(User).filter(User.id == user_id).first()
def get_all_posts_by_user(db: Session, user_id: int) -> list:
return db.query(Post).filter(Post.user_id == user_id).all()
# Eager loading для избежания N+1 problem
from sqlalchemy.orm import joinedload
def get_user_with_posts(db: Session, user_id: int) -> User:
return db.query(User).options(
joinedload(User.posts)
).filter(User.id == user_id).first()
Плюсы ORM:
- Безопасность от SQL injection
- Абстракция от БД (легче менять БД)
- Автоматическое управление отношениями
- Типизация в Python
Минусы ORM:
- Медленнее сырого SQL в сложных запросах
- N+1 проблемы, если неправильно использовать
- Сложнее оптимизировать
2. SQLAlchemy Core (expression language)
Промежуточный уровень. Работаю с выражениями, а не объектами.
from sqlalchemy import select, insert, update, delete, and_, or_
from sqlalchemy.orm import Session
# SELECT
stmt = select(User).where(User.email.like('%@gmail.com'))
users = db.execute(stmt).scalars().all()
# INSERT
stmt = insert(User).values(
username='john',
email='john@example.com'
)
result = db.execute(stmt)
db.commit()
# UPDATE
stmt = update(User).where(
User.id == 5
).values(
email='newemail@example.com'
)
db.execute(stmt)
db.commit()
# DELETE
stmt = delete(User).where(User.id == 5)
db.execute(stmt)
db.commit()
# Сложные условия
stmt = select(User).where(
and_(
User.email.like('%@gmail.com'),
User.created_at > some_date
)
).order_by(User.created_at.desc())
JOIN в Core:
from sqlalchemy import join
stmt = select(User, Post).join(
Post, User.id == Post.user_id
).where(User.id == 1)
results = db.execute(stmt).all()
3. Сырой SQL (Raw SQL)
Для сложных запросов или специфичных оптимизаций.
from sqlalchemy import text
# Простой запрос
stmt = text('SELECT * FROM users WHERE email = :email')
result = db.execute(stmt, {'email': 'john@example.com'}).first()
# Сложный запрос с подзапросами
query = text('''
SELECT u.id, u.username, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
WHERE u.created_at > :date
GROUP BY u.id
ORDER BY post_count DESC
LIMIT 10
''')
results = db.execute(query, {'date': '2024-01-01'}).fetchall()
Мой практический подход
ORM использую для:
- CRUD операции (create, read, update, delete)
- Простые фильтры и поиск
- Работа с отношениями
- Быстрое прототипирование
Core использую для:
- Bulk операции (много данных за раз)
- Сложные условия
- JOINs со множеством таблиц
- Когда нужна большая производительность
Raw SQL использую для:
- Window functions
- CTEs (Common Table Expressions)
- Специфичные оптимизации БД
- Миграции (всегда сырой SQL)
Реальный пример из production
from sqlalchemy import and_, or_, func
from sqlalchemy.orm import Session
def search_products(db: Session, query: str, category: str, min_price: float):
# Комбинирую ORM и Core
stmt = select(Product).where(
and_(
or_(
Product.name.ilike(f'%{query}%'),
Product.description.ilike(f'%{query}%')
),
Product.category == category,
Product.price >= min_price
)
).order_by(
Product.rating.desc()
).limit(50)
return db.execute(stmt).scalars().all()
# Для analytics — raw SQL
def get_daily_sales(db: Session, start_date, end_date):
query = text('''
SELECT DATE(created_at) as date, SUM(amount) as total
FROM orders
WHERE created_at BETWEEN :start AND :end
GROUP BY DATE(created_at)
ORDER BY date ASC
''')
return db.execute(
query,
{'start': start_date, 'end': end_date}
).fetchall()
Асинхронность (async SQLAlchemy)
В современных проектах часто использую async:
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
engine = create_async_engine('postgresql+asyncpg://user:pass@localhost/db')
async def get_user_async(session: AsyncSession, user_id: int):
stmt = select(User).where(User.id == user_id)
result = await session.execute(stmt)
return result.scalar_one_or_none()
Выбор уровня зависит от баланса между удобством (ORM) и производительностью (Raw SQL). Обычно начинаю с ORM, затем оптимизирую при необходимости.