JavaRush /Курси /Модуль 4: FastAPI /Визначення полів і типів даних у моделях SQLAlchemy

Визначення полів і типів даних у моделях SQLAlchemy

Модуль 4: FastAPI
Рівень 6 , Лекція 3
Відкрита

Ми вже говорили, що у SQLAlchemy є дуже багато вбудованих типів даних, які відповідають типам даних в SQL. Ці типи визначають, як саме твої дані будуть зберігатися в базі даних. Наприклад, рядки, цілі числа, дати і часи, булеві значення і навіть складні об'єкти, такі як JSON.


Основні типи даних

Давай розглянемо таблицю з основними типами даних і одразу спробуємо застосувати деякі з них на практиці.

Тип SQLAlchemy Опис
String(size) Рядкове поле, де size задає максимальну довжину
Text Необмежене текстове поле
Integer Ціле число
Float Число з плаваючою комою
Boolean Булеве значення True/False
Date Дата (без часу)
DateTime Дата й час
JSON Поле для зберігання JSON-об'єктів
LargeBinary Для зберігання двійкових даних (наприклад, зображень)
Enum Поле для перелічень (enums), наприклад, "Активний/Неактивний"

А ось приклад використання. Можеш спробувати розширити його або створити свій власний.


from sqlalchemy import Column, Integer, String, Date, Boolean

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))  # Максимальна довжина 50 символів
    email = Column(String(120), unique=True, nullable=False)  # Унікальний і обов'язковий
    birth_date = Column(Date)  # Поле для дати народження
    is_active = Column(Boolean, default=True)  # Прапорець активності користувача

Ось так просто — ми описали модель User з основними типами даних. Тепер давай розберемо деталі, додамо трохи магії і подивимось, як працювати з обмеженнями й валідацією.


Приклад розширеного визначення моделі

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

Складна модель:


from sqlalchemy import Column, Integer, Float, DateTime, String, ForeignKey, Boolean
from sqlalchemy.orm import relationship
import datetime

class Product(Base):
    __tablename__ = "product"
    id = Column(Integer, primary_key=True)
    name = Column(String(100), nullable=False)  # Назва продукту
    price = Column(Float, nullable=False)  # Ціна продукту
    created_at = Column(DateTime, default=datetime.datetime.utcnow)  # Час створення
    is_available = Column(Boolean, default=True)  # Прапорець наявності товару

    # Зв'язок з таблицею категорій
    category_id = Column(Integer, ForeignKey('category.id'))
    category = relationship("Category", back_populates="products")

class Category(Base):
    __tablename__ = "category"
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique=True, nullable=False)  # Назва категорії
    products = relationship("Product", back_populates="category")  # Зв'язок з продуктами

Тут ми використовуємо:

  • Зовнішній ключ ForeignKey для зв'язку продуктів з категоріями.
  • Відношення через relationship, щоб створити асоціацію між двома таблицями.
  • Поля з обмеженнями, такими як унікальність (unique=True) і обов'язковість (nullable=False).

Валідація та обмеження

Іноді нам потрібно більше, ніж просто поля. Наприклад, інколи треба обмежити унікальність у базі даних або задати діапазон значень. SQLAlchemy дозволяє це робити "на льоту".

Унікальність:


email = Column(String(120), unique=True)

Тепер ніхто не зможе зареєструвати два акаунти з одним email-адресою.

Обмеження довжини:


username = Column(String(20), nullable=False)

Тут ми обмежили довжину імені користувача до 20 символів і зробили поле обов'язковим.

Щоб пришвидшити запити, можна додати індекси:


surname = Column(String(50), index=True)

Значення за замовчуванням:


created_at = Column(DateTime, default=datetime.datetime.utcnow)

а тепер давай використаємо все разом:


from sqlalchemy import Column, Integer, String, Float, DateTime, CheckConstraint

class Order(Base):
    __tablename__ = 'order'
    id = Column(Integer, primary_key=True)
    product_id = Column(Integer, nullable=False)
    quantity = Column(Integer, CheckConstraint('quantity > 0'), nullable=False)  # Кількість має бути > 0
    total_price = Column(Float, nullable=False)
    created_at = Column(DateTime, default=datetime.datetime.utcnow)

Користувацькі типи даних

Іноді треба створити свій власний тип даних. Наприклад, хочеш додати тип для зберігання "кольору" у форматі HEX.

Реалізація користувацького типу:


from sqlalchemy.types import TypeDecorator, String

class ColorType(TypeDecorator):
    impl = String

    def process_bind_param(self, value, dialect):
        if not value.startswith("#"):
            raise ValueError("Колір має бути у форматі HEX і починатися з #.")
        return value

    def process_result_value(self, value, dialect):
        return value.upper()  # Завжди повертати колір у верхньому регістрі

Використання в моделі:


class Theme(Base):
    __tablename__ = 'theme'
    id = Column(Integer, primary_key=True)
    name = Column(String(50), nullable=False)
    primary_color = Column(ColorType, nullable=False)  # Поле для кольору

Таким чином, ти можеш додавати будь-яку поведінку до своїх типів даних.


Важливо знати: типові помилки

У процесі роботи зі схемами і типами даних можна нарватися на неприємності. Наприклад:

  • Помилка узгодження типів: якщо ти намагаєшся використати рядок у полі, що очікує Integer, отримаєш виключення. Завжди перевіряй відповідність типів у своїй моделі й даних.
  • Унікальні обмеження: додаючи unique=True, врахуй, що в базі даних теж має бути індекс для цього. SQLAlchemy робить це автоматично, але якщо ти забув, індексація буде повільною.
  • Обмеження NOT NULL: якщо поле позначене як nullable=False, а ти не передаси значення, запит завершиться помилкою.

SQLAlchemy — це потужний інструмент, який допомагає не лише працювати з даними, але й задавати строгі обмеження, покращувати продуктивність і навіть навчати розробників писати більш чіткі й продумані моделі даних. На наступному занятті ми будемо використовувати ці моделі для виконання CRUD операцій.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ