Object-Relational Mapping, или ORM, – это техника программирования, которая позволяет разработчикам работать с базами данных, используя объектно-ориентированные принципы. С ORM вы можете манипулировать данными в базе данных, как если бы это были обычные объекты в вашем коде. Это устраняет необходимость писать громоздкие SQL-запросы вручную, делая код более читаемым и легким для сопровождения.

Представьте себе типичную задачу: вы хотите получить список пользователей из базы данных, отсортированный по имени. Без ORM вам пришлось бы писать что-то вроде этого:

SELECT * FROM users ORDER BY name;

А затем преобразовывать результат в объекты на языке программирования, таком как Python. С ORM этот процесс автоматизируется, и вы можете сделать это одной строкой кода:

users = session.query(User).order_by(User.name).all()

Зачем использовать ORM?

  1. Удобство и скорость разработки: ORM позволяет вам писать меньше кода и быстрее переходить от идеи к работающему приложению. Вам не нужно беспокоиться о написании и оптимизации SQL-запросов – ORM сделает это за вас.
  2. Поддерживаемость кода: Когда вы используете ORM, ваш код становится более читаемым и логичным. Вы работаете с объектами и методами, а не с сырыми SQL-запросами, что упрощает понимание и поддержку кода.
  3. Безопасность: ORM автоматически обрабатывает параметры запросов, что снижает риск SQL-инъекций – одной из самых распространенных уязвимостей в веб-приложениях.
  4. Кросс-база данных совместимость: С ORM вы можете легко переключаться между различными системами управления базами данных (например, MySQL, PostgreSQL, SQLite), практически не изменяя код приложения.

SQLAlchemy – это одна из самых популярных и мощных библиотек для работы с базами данных в языке Python. Она предлагает два основных способа взаимодействия с базой данных: Core и ORM. SQLAlchemy Core предоставляет низкоуровневый интерфейс для работы с SQL-запросами, а ORM позволяет работать с базой данных через объектно-ориентированный интерфейс.

Установка и настройка

Как установить SQLAlchemy через pip

Установка SQLAlchemy очень проста и занимает всего несколько минут. SQLAlchemy распространяется через пакетный менеджер pip, что позволяет установить его одной командой в терминале:

pip install sqlalchemy

Если вы используете виртуальное окружение (virtual environment), убедитесь, что оно активировано перед установкой. Виртуальное окружение помогает изолировать зависимости проекта, что особенно полезно при работе над несколькими проектами с различными зависимостями.

Настройка базового окружения

После установки SQLAlchemy, необходимо настроить базовое окружение для работы с базой данных. Для этого создадим простой Python-скрипт. Пусть это будет файл main.py. В этом файле мы подключимся к базе данных и создадим сессию для взаимодействия с ней.

Импорт необходимых модулей:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base

Создание движка:

Движок (Engine) – это основной компонент SQLAlchemy, который отвечает за связь с базой данных. Мы будем использовать SQLite для простоты, но SQLAlchemy поддерживает множество других баз данных, таких как PostgreSQL, MySQL и др.

engine = create_engine('sqlite:///example.db')

Здесь мы создаем движок, который будет работать с файлом базы данных example.db. Если файл не существует, он будет создан автоматически.

Создание декларативной базы:

Декларативная база (Declarative Base) – это базовый класс для всех моделей, которые мы будем определять. Она предоставляет основные возможности для работы с таблицами и маппингом.

Base = declarative_base()

Создание сессии:

Сессия (Session) – это объект, который используется для всех операций с базой данных. Она является посредником между вашим кодом и движком.

Session = sessionmaker(bind=engine)
session = Session()

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

Подключение к базе данных

SQLAlchemy поддерживает множество различных баз данных. Вот несколько примеров строк подключения к различным базам данных:

PostgreSQL:

engine = create_engine('postgresql://username:password@localhost/mydatabase')

MySQL:

engine = create_engine('mysql+pymysql://username:password@localhost/mydatabase')

SQLite:

engine = create_engine('sqlite:///example.db')

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

Пример подключения и настройки

Создадим пример файла app.py, который демонстрирует все вышеперечисленные шаги:

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

# Создание движка
engine = create_engine('sqlite:///example.db')

# Создание декларативной базы
Base = declarative_base()

# Определение модели
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)

# Создание таблиц
Base.metadata.create_all(engine)

# Создание сессии
Session = sessionmaker(bind=engine)
session = Session()

# Добавление нового пользователя
new_user = User(name='John Doe')
session.add(new_user)
session.commit()

Основные концепции SQLAlchemy

В основе SQLAlchemy три компонента:

  • Engine (движок): основной компонент SQLAlchemy, который управляет подключением к базе данных и выполняет SQL-запросы. Движок инкапсулирует всю информацию, необходимую для связи с базой данных, такую как URL подключения, драйвер базы данных и параметры конфигурации.
  • Session (сессия): фабрика для создания новых объектов сессии, которые обеспечивают безопасное и управляемое взаимодействие с базой данных. Сессия используется для выполнения всех операций с базой данных, таких как добавление, обновление, удаление и запрос данных. Она также управляет транзакциями, что позволяет гарантировать целостность данных.
  • Declarative Base (декларативная база): базовый класс, от которого наследуются все модели. Он предоставляет декларативный способ определения моделей и таблиц, что упрощает и делает код более читаемым.

Модели

Модели в SQLAlchemy представляют собой классы Python, которые определяют структуру таблиц базы данных и их взаимоотношения. Они позволяют работать с базой данных как с объектами Python, абстрагируя сложность SQL-запросов и предоставляя удобный интерфейс для манипуляции данными.

Основные аспекты моделей в SQLAlchemy:

  • Определение структуры таблиц: Модели содержат информацию о столбцах таблицы, их типах и атрибутах. Каждый столбец в таблице базы данных представлен атрибутом класса модели.
  • Отражение таблиц в базе данных: Модели используются для создания таблиц в базе данных или для отражения существующих таблиц. Это позволяет использовать ORM (Object-Relational Mapping) для работы с данными.
  • Связи между таблицами: Модели могут определять отношения между таблицами, такие как один-к-одному, один-ко-многим и многие-ко-многим. Это упрощает работу с связанными данными.
  • Удобное манипулирование данными: Использование моделей позволяет манипулировать данными в базе данных через объекты Python, что делает код более читаемым и понятным.

Создание моделей

Начнем с базовой настройки и затем создадим модели для пользователей (User) и адресов (Address).

Шаг 1: Импорт необходимых модулей

Импортируем модули из SQLAlchemy, которые понадобятся для создания моделей.

from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship

Шаг 2: Создание базового класса для моделей

Создаем базовый класс, от которого будут наследоваться все наши модели. Этот класс предоставляется SQLAlchemy и используется для определения наших таблиц.

# Базовый класс для всех моделей
Base = declarative_base()

Шаг 3: Определение модели User

Создаем модель User, которая будет представлять таблицу users в базе данных.

# Определяем модель User
class User(Base):
    __tablename__ = 'users'  # Имя таблицы в базе данных

    id = Column(Integer, primary_key=True, index=True)  # Первичный ключ
    name = Column(String, index=True)  # Имя пользователя
    email = Column(String, unique=True, index=True)  # Уникальная электронная почта
    age = Column(Integer)  # Возраст пользователя

    # Метод для удобного текстового представления объекта
    def __repr__(self):
        return f"<User(name={self.name}, email={self.email}, age={self.age})>"

Шаг 4: Определение модели Address

Создадим модель Address, которая будет хранить адреса пользователей и демонстрировать отношения между таблицами.

# Определяем модель Address
class Address(Base):
    __tablename__ = 'addresses'  # Имя таблицы в базе данных

    id = Column(Integer, primary_key=True, index=True)  # Первичный ключ
    user_id = Column(Integer, ForeignKey('users.id'))  # Внешний ключ, ссылающийся на таблицу users
    address = Column(String, index=True)  # Адрес пользователя

    # Устанавливаем связь с моделью User
    user = relationship('User', back_populates='addresses')

    # Метод для удобного текстового представления объекта
    def __repr__(self):
        return f"<Address(user_id={self.user_id}, address={self.address})>"

Шаг 5: Установление связей в модели User

Чтобы завершить создание отношений, добавим обратную связь в модель User.

# Добавляем связь в модели User
User.addresses = relationship('Address', back_populates='user')

Типы колонок и их атрибутов

При создании моделей в SQLAlchemy мы используем различные типы колонок и атрибуты для их настройки. Давайте подробно рассмотрим, какие бывают типы колонок и атрибуты.

Типы колонок (Columns):

  • Integer: целое число
  • String: строка (текст)
  • Float: число с плавающей точкой
  • Boolean: логическое значение (True/False)
  • Date: дата
  • DateTime: дата и время
  • Text: длинный текст
  • Enum: перечисление
  • ForeignKey: внешний ключ, связывающий с другой таблицей

Атрибуты колонок:

  • primary_key: Указывает, что колонка является первичным ключом.
  • unique: Указывает, что значения в колонке должны быть уникальными.
  • index: Создает индекс для колонки, что ускоряет операции поиска.
  • nullable: Указывает, может ли колонка содержать NULL значения.
  • default: Устанавливает значение по умолчанию для колонки.

CRUD-операции

CRUD-операции (Create, Read, Update, Delete) — это основные действия, которые выполняются с данными в базе данных. Рассмотрим их на примере модели пользователя (User) в SQLAlchemy.

Create (Создание)

Для создания нового пользователя в базе данных мы будем использовать метод add() для добавления нового объекта в сессию и метод commit() для сохранения изменений в базе данных.

def create_user(db_session, name, email, age):
    # Создаем экземпляр модели User
    new_user = User(name=name, email=email, age=age)
    db_session.add(new_user)  # Добавляем пользователя в сессию
    db_session.commit()  # Фиксируем изменения в базе данных
    db_session.refresh(new_user)  # Обновляем объект, чтобы получить присвоенный id
    return new_user

# Пример использования
db = SessionLocal()
user = create_user(db, name="Alice", email="alice@example.com", age=25)
print(user)
db.close()

Read (Чтение)

Для чтения данных мы используем метод query(). Рассмотрим несколько способов получения данных.

def get_user_by_id(db_session, user_id):
    return db_session.query(User).filter(User.id == user_id).first()

def get_user_by_email(db_session, email):
    return db_session.query(User).filter(User.email == email).first()

def get_all_users(db_session):
    return db_session.query(User).all()

# Пример использования
db = SessionLocal()
user = get_user_by_id(db, user_id=1)
print(user)

user = get_user_by_email(db, email="alice@example.com")
print(user)

users = get_all_users(db)
print(users)
db.close()

Update (Обновление)

Для обновления данных мы сначала получаем объект, изменяем его атрибуты, затем фиксируем изменения в базе данных с помощью метода commit().

def update_user(db_session, user_id, name=None, email=None, age=None):
    user = db_session.query(User).filter(User.id == user_id).first()
    if user:
        if name:
            user.name = name
        if email:
            user.email = email
        if age is not None:
            user.age = age
        db_session.commit()  # Фиксируем изменения в базе данных
        db_session.refresh(user)  # Обновляем объект
    return user

# Пример использования
db = SessionLocal()
user = update_user(db, user_id=1, name="Alice Smith", age=26)
print(user)
db.close()

Delete (Удаление)

Для удаления данных мы получаем объект, удаляем его из сессии с помощью метода delete() и фиксируем изменения в базе данных с помощью метода commit()

def delete_user(db_session, user_id):
    user = db_session.query(User).filter(User.id == user_id).first()
    if user:
        db_session.delete(user)  # Удаляем пользователя из сессии
        db_session.commit()  # Фиксируем изменения в базе данных
    return user

# Пример использования
db = SessionLocal()
user = delete_user(db, user_id=1)
print(user)
db.close()