← Назад к вопросам

При работе с PostgreSQL использовал ORM или писал сырые SQL-запросы

1.7 Middle🔥 231 комментариев
#Python Core#Базы данных (SQL)

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

При работе с PostgreSQL использовал ORM или писал сырые SQL-запросы?

Использую оба подхода, в зависимости от задачи. Нет универсального решения.

ORM (Object-Relational Mapping)

Использую ORM для:

  • CRUD операций (Create, Read, Update, Delete)
  • Простых и средних запросов
  • Быстрой разработки
  • Защиты от SQL-инъекций
  • Связей между таблицами

Популярные Python ORM:

  • SQLAlchemy (самый мощный)
  • Django ORM (встроен в Django)
  • Tortoise ORM (асинхронный)

Пример с SQLAlchemy:

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker

class User:
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    name = Column(String)
    email = Column(String)

engine = create_engine("postgresql://user:pass@localhost/db")
Session = sessionmaker(bind=engine)
session = Session()

# CREATE
user = User(name="Alice", email="alice@example.com")
session.add(user)
session.commit()

# READ
users = session.query(User).filter(User.name == "Alice").all()

# UPDATE
user = session.query(User).filter(User.id == 1).first()
user.email = "new@example.com"
session.commit()

# DELETE
session.query(User).filter(User.id == 1).delete()
session.commit()

Сырые SQL-запросы

Пишу сырые SQL для:

  • Сложных запросов (CTE, window functions, группировки)
  • Оптимизации производительности
  • PostgreSQL-специфичных функций
  • Аналитических запросов

Пример с SQLAlchemy:

from sqlalchemy import text

query = text("""
    SELECT 
        users.id,
        users.name,
        COUNT(orders.id) as order_count
    FROM users
    LEFT JOIN orders ON users.id = orders.user_id
    WHERE users.created_at > :date
    GROUP BY users.id
    HAVING COUNT(orders.id) > :min_orders
    ORDER BY order_count DESC
""")

result = session.execute(query, {"date": "2024-01-01", "min_orders": 5})
for row in result:
    print(row)

Сравнение

КритерийORMRaw SQL
Скорость разработкиБыстроМедленнее
SQL-инъекцииЗащищеноНужно следить
ПроизводительностьМожет быть медленнееОптимальнее
Сложные запросыЗатруднительноЕстественно
ЧитаемостьХорошоЗависит

Мой гибридный подход

Начинаю с ORM, переходу на SQL если нужна оптимизация.

class UserRepository:
    def __init__(self, session):
        self.session = session
    
    # Простой запрос — ORM
    def get_by_id(self, user_id: int):
        return self.session.query(User).filter(User.id == user_id).first()
    
    # Средний запрос — ORM
    def get_active_users(self):
        return self.session.query(User).filter(
            (User.is_active == True) & (User.verified == True)
        ).all()
    
    # Сложный аналитический запрос — сырой SQL
    def get_statistics(self, start_date, end_date):
        query = text("""
            SELECT 
                DATE_TRUNC("day", created_at) as date,
                COUNT(*) as new_users,
                COUNT(CASE WHEN verified = true THEN 1 END) as verified
            FROM users
            WHERE created_at BETWEEN :start_date AND :end_date
            GROUP BY DATE_TRUNC("day", created_at)
            ORDER BY date
        """)
        
        return self.session.execute(query, {
            "start_date": start_date,
            "end_date": end_date
        }).fetchall()

Лучшие практики

1. Всегда используй параметры, не строковую интерполяцию

# Правильно
query = text("SELECT * FROM users WHERE id = :user_id")
result = session.execute(query, {"user_id": user_id})

# Неправильно
result = session.execute(f"SELECT * FROM users WHERE id = {user_id}")

2. Профилируй запросы в production

Включи логирование SQL в development:

import logging
logging.basicConfig()
logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO)

3. Используй индексы для сложных запросов

CREATE INDEX idx_users_created_at ON users(created_at);
CREATE INDEX idx_orders_user_id ON orders(user_id);

Распределение подходов

Мой опыт показывает:

  • 70% ORM для обычных CRUD операций
  • 30% Raw SQL для сложных аналитических запросов
  • Всегда используй параметризованные запросы
  • Оптимизируй только когда есть проблемы с производительностью
  • Документируй сложные SQL запросы

Заключение

Выбираю инструмент в зависимости от задачи. ORM удобен для типовых операций, SQL дает контроль и производительность для сложных случаев.