JavaRush /Курсы /Модуль 4: FastAPI /Создание реляционных моделей и использование связей (Fore...

Создание реляционных моделей и использование связей (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 позволяет не только упрощённо взаимодействовать с базой данных, но также сохраняет возможность оптимизации и управления сложными реляционными структурами. С этим инструментом ваши базы данных будут работать не только правильно, но и красиво.

1
Задача
Модуль 4: FastAPI, 6 уровень, 7 лекция
Недоступна
Работа с отношением "Один ко многим"
Работа с отношением "Один ко многим"
1
Задача
Модуль 4: FastAPI, 6 уровень, 7 лекция
Недоступна
Создание связи "Многие ко многим"
Создание связи "Многие ко многим"
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ