You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SQLAlchemy中实现多对多?含用户/电影/系列的观看列表设计

实现用户观影清单(Watchlist)的最佳方案

场景描述

你已拥有users、movies、series三张表,需要创建Watchlist表记录用户计划观看的电影和剧集,不清楚三张表关联的最佳实现方式,同时想确认多对多关系是否适用于该场景。

两种常见实现方案

方案一:拆分两个多对多关联表

将用户与电影、用户与剧集的观影关联分开维护,这是数据库设计中更规范的方案,符合单一职责原则。

对应的SQLAlchemy代码实现:

from sqlalchemy import ForeignKey, DateTime
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.ext.declarative import declarative_base
import datetime

Base = declarative_base()

class User(Base):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True)
    # 关联用户的电影观影清单
    watchlist_movies = relationship("UserMovieWatchlist", back_populates="user")
    # 关联用户的剧集追剧清单
    watchlist_series = relationship("UserSeriesWatchlist", back_populates="user")

class Movie(Base):
    __tablename__ = "movies"
    id: Mapped[int] = mapped_column(primary_key=True)
    watchlist_entries = relationship("UserMovieWatchlist", back_populates="movie")

class Series(Base):
    __tablename__ = "series"
    id: Mapped[int] = mapped_column(primary_key=True)
    watchlist_entries = relationship("UserSeriesWatchlist", back_populates="series")

# 用户-电影观影清单中间表
class UserMovieWatchlist(Base):
    __tablename__ = "user_movie_watchlist"
    user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), primary_key=True)
    movie_id: Mapped[int] = mapped_column(ForeignKey("movies.id"), primary_key=True)
    # 可选字段:记录添加时间、备注等
    added_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
    user = relationship("User", back_populates="watchlist_movies")
    movie = relationship("Movie", back_populates="watchlist_entries")

# 用户-剧集追剧清单中间表
class UserSeriesWatchlist(Base):
    __tablename__ = "user_series_watchlist"
    user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), primary_key=True)
    series_id: Mapped[int] = mapped_column(ForeignKey("series.id"), primary_key=True)
    added_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
    user = relationship("User", back_populates="watchlist_series")
    series = relationship("Series", back_populates="watchlist_entries")

优点:结构清晰,查询、维护简单;后续扩展(比如给电影和剧集的清单添加不同字段)更灵活;外键约束能保证数据完整性。
缺点:需要维护两张中间关联表。

方案二:单张Watchlist表加类型标记

用一张表统一管理用户的观影和追剧清单,通过content_type字段区分是电影还是剧集,content_id关联对应实体的ID。

对应的SQLAlchemy代码实现:

from sqlalchemy import ForeignKey, String, DateTime
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.ext.declarative import declarative_base
import datetime

Base = declarative_base()

class User(Base):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True)
    watchlist = relationship("Watchlist", back_populates="user")

class Movie(Base):
    __tablename__ = "movies"
    id: Mapped[int] = mapped_column(primary_key=True)

class Series(Base):
    __tablename__ = "series"
    id: Mapped[int] = mapped_column(primary_key=True)

class Watchlist(Base):
    __tablename__ = "watchlist"
    id: Mapped[int] = mapped_column(primary_key=True)
    user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
    # 标记内容类型:'movie' 或 'series'
    content_type: Mapped[str] = mapped_column(String(20))
    # 关联对应电影或剧集的ID
    content_id: Mapped[int] = mapped_column()
    added_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
    user = relationship("User", back_populates="watchlist")

    # 可选:添加属性快速获取关联内容
    @property
    def content(self):
        if self.content_type == 'movie':
            return self.session.query(Movie).get(self.content_id)
        elif self.content_type == 'series':
            return self.session.query(Series).get(self.content_id)
        return None

优点:仅需维护一张表,统一管理所有观影清单。
缺点:无法通过外键约束保证content_id对应有效实体;查询时需额外判断类型,扩展性较差。

多对多关系是否适用?

这个场景完全适合使用多对多关系:

  • 一个用户可以计划观看多部电影/多个剧集
  • 一部电影/一个剧集可以被多个用户加入清单
    这完全符合多对多关系的典型特征。方案一就是标准的多对多实现(通过中间表建立用户与电影、用户与剧集的多对多关联);方案二本质也是多对多,但采用了更灵活的设计,牺牲了部分数据约束性。

内容的提问来源于stack exchange,提问作者123 123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 06:26:06