Модели данных можно смело назвать основой приложения. Именно они определяют форму и структуру данных, с которыми оно будет работать. Ну а SQLAlchemy — это такой удобный способ описания этих моделей с помощью Python-классов. При этом SQLAlchemy спроектирован так, что вам — разработчику — не нужно концентрироваться на сложностях SQL, можно сосредоточить внимание на логике.
Как классы в ООП, модели данных напоминают формочки для печенья. Вы определяете "формочку" (то есть модель) один раз, а затем используете её для создания множества однотипных "печенек" (то есть конкретных записей в базе данных).
Основы создания моделей
Каждая модель SQLAlchemy — это класс, который наследуется от базового класса Base. Этот базовый класс отвечает за связь модели с таблицей в базе данных. В SQLAlchemy для создания Base используется конструкция:
from sqlalchemy.orm import declarative_base
Base = declarative_base()
Давайте создадим нашу первую модель — модель пользователя (User).
from sqlalchemy import Column, Integer, String
class User(Base):
__tablename__ = 'users' # Указываем имя таблицы
id = Column(Integer, primary_key=True) # Первичный ключ
name = Column(String, nullable=False) # Столбец для имени
email = Column(String, unique=True, nullable=False) # Уникальный и обязательный столбец
def __repr__(self):
return f"<User(name={self.name!r}, email={self.email!r})>"
Что мы сделали:
- Указали имя таблицы через
__tablename__. - Определили поля таблицы (столбцы) как атрибуты класса, используя
Column. - Добавили типы данных для каждого столбца (
Integer,String). - Реализовали метод
__repr__для более удобного отображения объекта.
Теперь модель User представляет таблицу users в нашей базе данных.
Определение таблиц и столбцов
В таблицах базы данных есть разные типы данных. SQLAlchemy поддерживает множество типов: Integer, String, Float, Boolean, DateTime и другие. Давайте разберём, как использовать эти типы и добавлять дополнительные поля.
Допустим, наша модель пользователя также должна хранить дату регистрации и статус, указывающий, активен ли пользователь. Добавим их:
from sqlalchemy import Boolean, DateTime
from datetime import datetime
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
email = Column(String, unique=True, nullable=False)
is_active = Column(Boolean, default=True) # Булевое значение с умолчанием
registered_at = Column(DateTime, default=datetime.utcnow) # Используем текущую дату
def __repr__(self):
return f"<User(name={self.name!r}, email={self.email!r})>"
Теперь таблица users будет содержать два новых столбца:
is_active— булевое значение для статуса активности;registered_at— дата и время регистрации.
Пример создания сложной модели
Реальная жизнь — это не только "имя" и "email". Ваши модели могут быть сложными, включая ограничения, индексы и даже пользовательские типы данных. Давайте создадим модель для продуктов, включающую цену и категорию.
from sqlalchemy import ForeignKey, Float
class Product(Base):
__tablename__ = 'products'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
price = Column(Float, nullable=False)
category_id = Column(Integer, ForeignKey('categories.id')) # Внешний ключ для категории
def __repr__(self):
return f"<Product(name={self.name!r}, price={self.price!r})>"
Эта модель показывает:
- Как использовать тип
Floatдля хранения чисел с плавающей точкой. - Как связать модель с другой таблицей через
ForeignKey.
Создание связанных таблиц
Для полноты картины нам нужно создать таблицу categories, на которую ссылается Product.
class Category(Base):
__tablename__ = 'categories'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False, unique=True)
def __repr__(self):
return f"<Category(name={self.name!r})>"
Теперь у нас есть модель Category, к которой привязаны продукты. Мы сможем создавать категории и связывать их с товарами.
Обработка ограничений и индексов
SQLAlchemy позволяет добавлять ограничения, такие как уникальность (unique) или условие обязательного наличия данных (nullable=False). Добавим индекс на поле email в модели пользователя:
from sqlalchemy import Index
Index('idx_email', User.email) # Создание индекса
Индексы ускоряют поиск определённых данных, например, электронной почты. Это особенно важно для полей, которые часто используются в запросах.
Практическая демонстрация
Давайте создадим небольшую базу данных SQLite и создадим таблицы на основе наших моделей.
from sqlalchemy import create_engine
# Подключение SQLite (или другой базы данных)
engine = create_engine('sqlite:///example.db', echo=True) # echo=True для логирования SQL
# Создание всех таблиц
Base.metadata.create_all(engine)
print("Таблицы успешно созданы!")
После выполнения кода мы увидим детальные логи выполнения SQL-запросов для создания таблиц.
Зачем это нужно?
Почему мы используем ORM и SQLAlchemy вместо ручного написания SQL? Потому что ORM:
- Упрощает чтение и поддержку кода.
- Позволяет абстрагироваться от конкретной базы данных.
- Автоматизирует многие рутинные задачи, такие как преобразование данных между Python и SQL.
В реальных проектах модели помогают разработчикам быстро добавлять новые функции, обеспечивать целостность данных и избегать сложностей при миграции на другую базу данных.
SQLAlchemy, в отличие от Django ORM, предоставляет больше контроля над SQL-запросами. Это делает его отличным выбором для сложных проектов.
Типичные ошибки
- Пропущенный
__tablename__. Если забыть указать имя таблицы, SQLAlchemy не поймёт, как связать модель с таблицей. - Несоответствие типов данных. Убедитесь, что типы данных в модели соответствуют типам в базе данных. Например,
Stringдолжен иметь ограничение длины для совместимости с некоторыми базами. - Отсутствие первичного ключа. Каждая таблица должна иметь хотя бы один первичный ключ (обычно поле
id).
Итоговая структура проекта
На этом этапе у нас есть:
- Таблица пользователей (
User), включающая имя, email, активность и дату регистрации. - Таблица продуктов (
Product) с ценой и категорией. - Таблица категорий (
Category) для связи.
В следующей лекции мы продолжим разбираться с полями и типами данных, включая кастомные типы, ограничения и валидации.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ