У реальному житті відносини між об'єктами бувають різні: "одна людина — один паспорт", "один користувач — багато замовлень", "багато студентів — навчаються у багатьох викладачів". У реляційних базах даних це виражається через відносини:
- OneToOne (Один-до-одного): кожний рядок в одній таблиці відповідає тільки одному рядку в іншій. Аналог — одна людина й її паспорт.
- ForeignKey (Один-до-багатьох): один рядок у таблиці A може бути пов'язаний з багатьма рядками в таблиці B. Наприклад, один користувач і його замовлення.
- ManyToMany (Багато-до-багатьох): багато рядків в одній таблиці можуть бути пов'язані з багатьма рядками в іншій. Наприклад, студенти відвідують багато курсів, а курси мають багато студентів.
Застосування зв'язків
Зв'язки дозволяють об'єднувати таблиці, забезпечуючи узгодженість даних. Уявіть хаос, якби ви зберігали всю інформацію в одній таблиці. Зв'язки спрощують архітектуру.
Реалізація зв'язків з використанням SQLAlchemy
У SQLAlchemy зв'язок "один-до-одного" створюється за допомогою relationship і ForeignKey. Розглянемо приклад:
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from database import Base
# Таблиця User
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
# Зв'язок з таблицею Profile
profile = relationship("Profile", back_populates="user", uselist=False)
# Таблиця Profile
class Profile(Base):
__tablename__ = 'profiles'
id = Column(Integer, primary_key=True)
bio = Column(String)
user_id = Column(Integer, ForeignKey('users.id'))
# Зв'язок з таблицею User
user = relationship("User", back_populates="profile")
У цьому коді:
- У користувача може бути тільки один профіль, і навпаки.
- Використовуємо
uselist=False, щоб вказати, що зв'язок "один-до-одного". back_populatesдозволяє двом пов'язаним таблицям "знати" одна одну.
Для реалізації "один-до-багатьох" ми використовуємо ForeignKey і relationship. Приклад:
class Author(Base):
__tablename__ = 'authors'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
# Зв'язок з книгами
books = relationship("Book", back_populates="author")
class Book(Base):
__tablename__ = 'books'
id = Column(Integer, primary_key=True)
title = Column(String, nullable=False)
author_id = Column(Integer, ForeignKey('authors.id'))
# Зв'язок з автором
author = relationship("Author", back_populates="books")
Пояснення:
- У одного автора може бути багато книг (наприклад, Дж. Роулінг написала більше ніж одну книгу).
ForeignKeyв таблиціBookстворює зовнішній ключ на таблицюAuthor.
Відносини "багато-до-багатьох" хитріші, бо вимагають проміжну таблицю. Ось як це робиться:
from sqlalchemy import Table
# Допоміжна таблиця
association_table = Table(
'student_course', Base.metadata,
Column('student_id', Integer, ForeignKey('students.id')),
Column('course_id', Integer, ForeignKey('courses.id'))
)
# Таблиця Student
class Student(Base):
__tablename__ = 'students'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
# Зв'язок з курсами
courses = relationship("Course", secondary=association_table, back_populates="students")
# Таблиця Course
class Course(Base):
__tablename__ = 'courses'
id = Column(Integer, primary_key=True)
title = Column(String, nullable=False)
# Зв'язок зі студентами
students = relationship("Student", secondary=association_table, back_populates="courses")
Пояснення:
- Допоміжна таблиця
association_tableзберігає зв'язки між студентами та курсами. - Параметр
secondaryвказує на цю таблицю.
Практичні застосування зв'язків
Припустимо, ви будуєте базу даних для e-commerce. У вас можуть бути такі зв'язки:
- Користувач (User) має замовлення (Orders) — "один-до-багатьох".
- Замовлення (Order) містить товари (Products) — "багато-до-багатьох".
- Профіль (Profile) прив'язаний до користувача — "один-до-одного".
Використовуючи SQLAlchemy, можна виразити це так:
class Order(Base):
__tablename__ = 'orders'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'))
user = relationship("User", back_populates="orders")
products = relationship("Product", secondary='order_product', back_populates="orders")
class Product(Base):
__tablename__ = 'products'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
price = Column(Integer)
orders = relationship("Order", secondary='order_product', back_populates="products")
order_product_table = Table(
'order_product', Base.metadata,
Column('order_id', Integer, ForeignKey('orders.id')),
Column('product_id', Integer, ForeignKey('products.id'))
)
Корисні поради
- Використовуйте зв'язки економно. Не завжди варто робити таблиці надто зв'язаними.
- Якщо дані однієї таблиці рідко використовуються, розгляньте можливість денормалізації.
- Для роботи з великими обсягами ManyToMany даних використовуйте індекси.
- Поширена помилка, якої варто уникати — забути вказати
back_populatesабоForeignKey. Це призведе до відсутності зв'язку між таблицями або до помилок під час виконання запитів.
Для тих, хто хоче копнути глибше, документація SQLAlchemy завжди готова прийти на допомогу!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ