JavaRush /Курси /Модуль 4: FastAPI /Створення реляційних моделей та використання зв'язків (Fo...

Створення реляційних моделей та використання зв'язків (ForeignKey)

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

У реальному світі сутності рідко існують в ізоляції, і наша база даних — не виняток. У реляційній моделі бази даних зовнішні ключі (ForeignKey) дозволяють пов'язувати записи між таблицями, створюючи відносини, такі як "один до одного", "один до багатьох", "багато до багатьох". Це як родинне дерево, родина, тільки без сварок за вечерею.

Наприклад:

  • У нас є таблиця Users, що містить інформацію про користувачів.
  • І таблиця Posts з записами блогу, які ці користувачі написали.
  • Кожен запис у Posts посилається на певного користувача з таблиці Users.

Типи відносин

1. Один до багатьох (One-to-Many). Кожен користувач може мати кілька постів, але кожен пост пов'язаний лише з одним користувачем.

2. Один до одного (One-to-One). Кожному користувачу відповідає лише один профіль у базі (наприклад, таблиця Profiles).

3. Багато до багатьох (Many-to-Many). Кожен користувач може підписатися на багатьох авторів, а кожен автор може мати багатьох підписників. Наприклад, таблиця Followers.


Приклади використання зв'язків

Давайте створимо базові моделі для користувачів і їхніх постів:


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, index=True)  # Первинний ключ
    name = Column(String, index=True)
    email = Column(String, unique=True, index=True)

    # Зв'язок з таблицею Posts
    posts = relationship("Post", back_populates="owner")

# Модель Post
class Post(Base):
    __tablename__ = 'posts'
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True)
    content = Column(String)
    user_id = Column(Integer, ForeignKey('users.id'))  # Зовнішній ключ

    # Зв'язок з користувачем
    owner = relationship("User", back_populates="posts")
  1. ForeignKey в Post
    Поле user_id в таблиці posts посилається на колонку id в таблиці users.
  2. relationship
    В моделі User і Post використовується relationship, щоб SQLAlchemy міг зрозуміти зв'язок між таблицями.
    • back_populates створює двосторонній зв'язок: це дозволяє переходити від користувача до його постів і назад.
  3. Живий зв'язок
    Тепер, якщо ми витягнемо об'єкт User з бази даних, ми можемо звернутися до його постів через user.posts. Зручно? Ще б!

Тепер створимо користувача і пару постів, пов'язаних з ним:


from sqlalchemy.orm import Session
from models import User, Post

# Функція для додавання даних
def create_user_with_posts(db: Session):
    # Створюємо користувача
    new_user = User(name="Аліса", email="alice@example.com")
    db.add(new_user)
    db.commit()
    db.refresh(new_user)

    # Створюємо пости, пов'язані з цим користувачем
    post1 = Post(title="Перший пост", content="Привіт, світ!", owner=new_user)
    post2 = Post(title="Ще один пост", content="Вчуся SQLAlchemy!", owner=new_user)
    db.add_all([post1, post2])
    db.commit()

# Використання функції
create_user_with_posts(db_session)

Тепер ми можемо отримати всі записи користувача "Аліса" (і її пости):


# Запит користувача
user = db.query(User).filter(User.name == "Аліса").first()

# Доступ до постів
print(user.posts)

# Вивід постів
for post in user.posts:
    print(f"Заголовок: {post.title}, Текст: {post.content}")

Запити з використанням зв'язків

Якщо хочеш виконати запит, який напряму зв'язує таблиці (наприклад, щоб повернути пости разом із іменем автора), SQLAlchemy дозволяє це зробити через join.


from sqlalchemy.orm import joinedload

# Запит всіх постів з даними користувача
posts = db.query(Post).options(joinedload(Post.owner)).all()

for post in posts:
    print(f"Заголовок: {post.title}, Автор: {post.owner.name}")

Робота з "багато до багатьох"

Іноді одного зв'язку замало. Наприклад, у системі підписок користувачі можуть підписуватися один на одного. Для цього створюється проміжна таблиця. Створімо її:


from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.orm import relationship

# Проміжна таблиця
followers_association = Table(
    'followers',
    Base.metadata,
    Column('follower_id', Integer, ForeignKey('users.id')),
    Column('followed_id', Integer, ForeignKey('users.id'))
)

# Модель User з відношенням багато до багатьох
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)

    # Зв'язок підписників
    followers = relationship(
        "User",
        secondary=followers_association,
        primaryjoin=id == followers_association.c.followed_id,
        secondaryjoin=id == followers_association.c.follower_id,
        backref="followed_by"
    )

Тепер додамо користувача і його підписників:


# Додавання підписників
def add_followers(db: Session):
    user1 = User(name="Боб")
    user2 = User(name="Чарлі")
    user3 = User(name="Дейв")

    user1.followers.append(user2)  # Чарлі підписаний на Боба
    user1.followers.append(user3)  # Дейв підписаний на Боба

    db.add_all([user1, user2, user3])
    db.commit()

Помилки та особливості роботи

  • Каскадне видалення
    Якщо ви намагаєтеся видалити запис, який пов'язаний з іншою таблицею, може виникнути помилка зовнішнього ключа. У таких випадках рекомендується використовувати каскадне видалення (cascade="all, delete").
  • Спроба оновити зовнішній ключ напряму
    Пам'ятайте, що зручніше працювати з relationship, ніж напряму оновлювати поле ForeignKey.
  • Проміжна таблиця
    Якщо в проміжній таблиці забути вказати зовнішній ключ, SQLAlchemy не буде знати про зв'язок, і вся магія зникне.

Практичне застосування

Робота з реляційними моделями і зв'язками — це хліб і масло будь-якого веб-застосунку, який працює з базами даних. Бо ізольовані дані на практиці зустрічаються вкрай рідко. Такі навички затребувані на співбесідах, коли потрібно показати, що ви вмієте моделювати реальні відносини між сутностями.

Для реальних проектів:

  • Можна будувати складні системи авторизації, де користувач пов'язаний з ролями та дозволами.
  • Або створювати табличні структури для електронної комерції, де замовлення пов'язані з користувачами та товарами.

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

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