Моделі даних можна впевнено назвати основою застосунку. Саме вони визначають форму і структуру даних, з якими він буде працювати. А 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) для зв'язку.
У наступній лекції ми продовжимо розбиратися з полями і типами даних, включаючи кастомні типи, обмеження та валідації.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ