← Назад к вопросам
Что такое ORM и какие ORM есть для Python?
1.6 Junior🔥 171 комментариев
#Архитектура и паттерны#Базы данных (SQL)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
ORM в Python
ORM (Object-Relational Mapping) — это технология, которая позволяет работать с реляционной базой данных, используя объекты языка программирования вместо прямого SQL. ORM преобразует строки таблиц БД в объекты Python и наоборот.
Зачем нужна ORM
# Без ORM: прямой SQL (сложнее)
import sqlite3
conn = sqlite3.connect("users.db")
cursor = conn.cursor()
# Создание
cursor.execute(
"INSERT INTO users (name, email) VALUES (?, ?)",
("Иван", "ivan@example.com")
)
conn.commit()
# Чтение
cursor.execute("SELECT * FROM users WHERE id = ?", (1,))
row = cursor.fetchone()
user = {"id": row[0], "name": row[1], "email": row[2]}
# Обновление
cursor.execute(
"UPDATE users SET email = ? WHERE id = ?",
("newemail@example.com", 1)
)
conn.commit()
# Удаление
cursor.execute("DELETE FROM users WHERE id = ?", (1,))
conn.commit()
conn.close()
# С ORM: объектно-ориентированный подход (проще)
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import declarative_base, Session
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String)
engine = create_engine("sqlite:///users.db")
session = Session(engine)
# Создание
user = User(name="Иван", email="ivan@example.com")
session.add(user)
session.commit()
# Чтение
user = session.query(User).filter(User.id == 1).first()
# Обновление
user.email = "newemail@example.com"
session.commit()
# Удаление
session.delete(user)
session.commit()
Преимущества ORM
✓ Работаешь с объектами, а не SQL строками
✓ Безопасность от SQL инъекций (автоматическая параметризация)
✓ Код независим от конкретной БД (SQLite, PostgreSQL, MySQL)
✓ Меньше кода для CRUD операций
✓ Встроенные отношения (relations) между объектами
✓ Валидация на уровне модели
✓ Миграции (в некоторых ORM)
Недостатки ORM
✗ Сложные запросы писать сложнее
✗ Производительность хуже, чем сырой SQL
✗ Можно случайно создать N+1 запросов
✗ Кривая обучения
✗ Некоторые ORM "магические" и сложны в отладке
ORM для Python
1. SQLAlchemy (самая популярная)
Мощная, гибкая, используется везде. Есть два стиля: ORM и Core.
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey, DateTime
from sqlalchemy.orm import declarative_base, Session, relationship
from datetime import datetime
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String(50), nullable=False)
email = Column(String(100), unique=True)
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, nullable=False)
user_id = Column(Integer, ForeignKey("users.id"))
author = relationship("User", back_populates="posts")
engine = create_engine("postgresql://user:password@localhost/mydb")
Base.metadata.create_all(engine)
with Session(engine) as session:
# Создание с отношением
user = User(name="Иван", email="ivan@example.com")
post = Post(title="Моя первая статья", author=user)
session.add(user)
session.commit()
# Чтение с eager loading
user = session.query(User).filter(User.name == "Иван").first()
print(user.posts) # Автоматически загружает посты
2. Django ORM
Проста в использовании, встроена в Django, но привязана к фреймворку.
# models.py
from django.db import models
class User(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts")
# Использование
user = User.objects.create(name="Иван", email="ivan@example.com")
post = Post.objects.create(title="Статья", author=user)
user = User.objects.get(name="Иван")
user.posts.all() # Все посты пользователя
user.email = "new@example.com"
user.save()
user.delete()
3. Peewee
Лёгкая и простая ORM, хорошая для небольших проектов.
from peewee import SqliteDatabase, Model, CharField, ForeignKeyField
db = SqliteDatabase("my_app.db")
class BaseModel(Model):
class Meta:
database = db
class User(BaseModel):
name = CharField()
email = CharField(unique=True)
class Post(BaseModel):
title = CharField()
author = ForeignKeyField(User, backref="posts")
db.create_tables([User, Post])
# Использование
user = User.create(name="Иван", email="ivan@example.com")
post = Post.create(title="Статья", author=user)
for post in user.posts:
print(post.title)
4. Tortoise ORM
Асинхронная ORM для async/await, похожа на Django ORM.
from tortoise import fields, run_async
from tortoise.models import Model
from tortoise.contrib.pydantic import pydantic_model_creator
class User(Model):
id = fields.IntField(pk=True)
name = fields.CharField(max_length=100)
email = fields.CharField(max_length=100, unique=True)
created_at = fields.DatetimeField(auto_now_add=True)
posts: fields.ReverseRelation["Post"]
class Post(Model):
id = fields.IntField(pk=True)
title = fields.CharField(max_length=200)
author: fields.ForeignKeyRelation[User] = fields.ForeignKeyField(
"models.User", related_name="posts"
)
async def main():
# Асинхронные операции
user = await User.create(name="Иван", email="ivan@example.com")
post = await Post.create(title="Статья", author=user)
user = await User.get(name="Иван")
posts = await user.posts.all()
print(posts)
run_async(main())
5. SQLModel
Комбинирует SQLAlchemy и Pydantic, современный подход.
from sqlmodel import SQLModel, Field, create_engine, Session
from typing import Optional
class User(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
email: str
engine = create_engine("sqlite:///database.db")
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
user = User(name="Иван", email="ivan@example.com")
session.add(user)
session.commit()
user = session.query(User).filter(User.name == "Иван").first()
print(user.dict()) # {'id': 1, 'name': 'Иван', 'email': 'ivan@example.com'}
Сравнение ORM
┌─────────────┬──────────────┬─────────────┬───────────┬───────────┐
│ ORM │ Популярность │ Простота │ Гибкость │ Async │
├─────────────┼──────────────┼─────────────┼───────────┼───────────┤
│ SQLAlchemy │ ★★★★★ │ ★★★☆☆ │ ★★★★★ │ Да │
│ Django ORM │ ★★★★★ │ ★★★★★ │ ★★★☆☆ │ Нет* │
│ Peewee │ ★★★☆☆ │ ★★★★☆ │ ★★★☆☆ │ Нет │
│ Tortoise │ ★★☆☆☆ │ ★★★★☆ │ ★★★☆☆ │ Да │
│ SQLModel │ ★★☆☆☆ │ ★★★★★ │ ★★★★☆ │ Да │
└─────────────┴──────────────┴─────────────┴───────────┴───────────┘
* Django ORM получит async в 5.0
Типичные проблемы ORM
N+1 Problem (N плюс 1 запросов)
# Плохо: N+1 запросов
users = session.query(User).all() # 1 запрос
for user in users:
print(user.posts) # N запросов (по одному на каждого пользователя!)
# Хорошо: 2 запроса
from sqlalchemy.orm import joinedload
users = session.query(User).options(joinedload(User.posts)).all()
# 1 запрос с JOIN, посты загружены
Lazy Loading (лишний запрос в неожиданном месте)
# Плохо
user = session.query(User).filter(User.id == 1).first()
for post in user.posts: # Если posts ещё не загружены → ещё один запрос!
print(post.title)
# Хорошо
from sqlalchemy.orm import selectinload
user = session.query(User).options(selectinload(User.posts)).filter(
User.id == 1
).first()
for post in user.posts: # Уже загружены, нет доп. запроса
print(post.title)
Когда использовать ORM vs Raw SQL
# Используй ORM для:
# - CRUD операций (Create, Read, Update, Delete)
# - Простых запросов с фильтрацией
# - Отношений между таблицами
# - Быстрой разработки
user = session.query(User).filter(User.age > 18).all()
# Используй Raw SQL для:
# - Сложных агрегаций
# - Оптимизации производительности
# - Специфичных для БД функций
# - Когда ORM создаёт неоптимальный SQL
from sqlalchemy import text
result = session.execute(
text("""
SELECT u.name, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
GROUP BY u.id
HAVING COUNT(p.id) > 5
""")
)
Резюме
- ORM: преобразует таблицы БД в Python объекты
- SQLAlchemy: самая мощная и популярная ORM
- Django ORM: проста, но привязана к Django
- Peewee: лёгкая для маленьких проектов
- Tortoise/SQLModel: асинхронные ORM для современных приложений
- Проблемы: N+1, lazy loading, неоптимальный SQL
- Совет: учись писать эффективные запросы, иногда raw SQL быстрее