【问题标题】:How do I model a many-to-many relationship over 3 tables in SQLAlchemy (ORM)?如何在 SQLAlchemy (ORM) 中对 3 个表的多对多关系建模?
【发布时间】:2012-04-17 06:26:43
【问题描述】:

我对 SQL 很陌生,对SQL Alchemy 完全陌生,但有一个即将使用这两种方法的项目。所以我想我写一些东西来让自己舒服。由于宿醉,我决定写一些东西来记录酒精含量。

我有 events,其中 users 参与并消费 drinks。这些是我的三个基本表(有一个帮助表 guestlist,用于 usersevents 之间的 m:n 关系)。

drinks 始终列出所有活动 中所有用户 可用的饮品(无需映射任何内容)。 用户是不时创建的,事件也是如此。所有用户都可以加入所有事件,所以我使用guestlist 表来映射这些。

现在回到问题的核心:我需要跟踪哪个用户在哪个时间喝了哪个饮料。我尝试用另一个表shots(见下文)解决这个问题,但我不确定这是否是一个好的解决方案。


(来源:anyimg.com

SQL Alchemy 中,它可能看起来有点像这样(或者不是,但这是我迄今为止想出的)::

guestlist_table = Table('guestlist', Base.metadata,
    Column('event_id', Integer, ForeignKey('events.id')),
    Column('user_id', Integer, ForeignKey('users.id'))
)

class Event(Base):

  __tablename__ = 'events'

  id = Column(Integer, primary_key=True)
  name = Column(String(80), nullable=False)
  started = Column(DateTime, nullable=False, index=True,
    default=datetime.datetime.now
  )
  users = relationship("User", secondary=guestlist_table, backref="events")
  # ...

class User(Base):

  __tablename__ = 'users'

  id = Column(Integer, primary_key=True)
  name = Column(String(50), nullable=False, unique=True, index=True)
  birthdate = Column(Date, nullable=False)
  weight = Column(Integer, nullable=False)
  sex = Column(Boolean, nullable=False)
  # ...

class Drink(Base):

  __tablename__ = 'drinks'

  id = Column(Integer, primary_key=True)
  name = Column(String(50), nullable=False)
  volume = Column(Numeric(5,2), nullable=False)
  percent = Column(Numeric(5,2), nullable=False)
  # ...

class Shots(Base):

  __tablename__ = 'shots'

  id = Column(Integer, primary_key=True)
  at = Column(DateTime, nullable=False,
    default=datetime.datetime.now
  )
  user_id = Column(Integer, ForeignKey('users.id'), index=True)
  event_id = Column(Integer, ForeignKey('events.id'), index=True) 
  drink_id = Column(Integer, ForeignKey('drinks.id'))
  user = relationship("User", backref="shots")
  event = relationship("Event", backref="shots")
  drink = relationship("Drink", uselist=False) # one-to-one, no backref needed

我很难找到一种好的方法来构建一个将事件用户饮料映射在一起的表格:我应该如何制定relationships,我该如何query呢?

问题是我觉得我忽略了一些东西。 坦率地说,我完全不知道如何查询

以下是我大部分时间会提出的问题:

  • 我至少需要获取事件中消耗的所有镜头(可能按用户排序)
  • 有时我还需要特定用户的所有镜头(可能按事件排序)
  • 还有很多计算:
    • 每个事件的拍摄次数
    • 每位用户的拍摄次数
    • 用户在活动中击倒的次数

shots 表是管理此问题的好方法吗?

【问题讨论】:

  • 您好@Brutus,根据我的观点,您的关系就像 1 个用户可以参加许多活动,而 1 个活动有很多用户(来宾列表)。 1 个活动有很多饮料,1 个饮料可以在许多活动中提供(酒单)。 1 个用户可以喝很多饮料,1 个饮料可以提供给多个用户(guestdrinklist)。这是正确的吗?
  • 所有活动都提供所有饮料,所以我想我可能不需要drinklist。你所谓的guestdrinklist 我试图在Shots 类中建模。把班级分成两张表会更好吗?
  • 如果所有活动都提供所有饮品,则无需指定饮品与活动之间的关系。如果您的字段的子集与其他实体相关联,则您必须指定关系,如果所有相关联,则不需要关系。您可以在饮料和用户之间建立关系。
  • 我认为 Victor 的回答很好地涵盖了这一切。事实上,查询各种报告应该很容易。如果您需要那些用于报告,我肯定会尝试在查询中执行这些操作,以便您为报告的每个部分获得尽可能少的 SQL 语句。但是,您可能希望将其中一些作为属性(请参阅docs.sqlalchemy.org/en/latest/orm/…)。你的模型很好。不过要添加的一个明确的事情是从Shotsguestlist 表的复合FK(下一条评论)。
  • __table_args__ = ( ForeignKeyConstraint( ["user_id", "event_id"], ["guestlist.user_id", "guestlist.event_id"], name="fk_guestlist", ), )

标签: python sql orm sqlalchemy


【解决方案1】:

我建议你画一个实体关系图,它会更清楚的人和你理解你的问题。

回答你的问题:

我至少需要获取事件中消耗的所有镜头(可能按用户排序)

要获得活动的所有镜头,您可以尝试

session.query(Shots).filter_by(event_id=event_id)

event_id 是你想查询的事件的id。按用户存储,可以试试

from sqlalchemy.sql.expression import desc, asc
session.query(Shots) \
    .filter_by(event_id=event_id) \
    .order_by(asc(Shots.user_id))

当然,你可能想按用户的属性排序,你可以加入用户表。

from sqlalchemy.sql.expression import desc, asc
session.query(Shots) \
    .filter_by(event_id=event_id) \
    .join(User) \
    .order_by(asc(User.name))

很简单。

我有时还需要特定用户的所有镜头(可能按事件排序)

和前面的例子一样

每个事件的拍摄次数

session.query(Shots) \
    .filter_by(event_id=event_id) \
    .count()

我没有运行它们,我只是在这里写的,但如果我没有打错,它们应该都可以工作。

【讨论】:

  • 感谢您的回答。我一回家就试一试。我还添加了一个 ERP。关于我的表格布局:考虑到我的查询案例,您认为shots 表格是一种将数据映射在一起的有效方式吗?还是应该使用其他方法来组织表格和关系?
猜你喜欢
  • 2021-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-16
  • 1970-01-01
  • 2010-12-25
  • 2019-01-12
  • 1970-01-01
相关资源
最近更新 更多