我如何在 SQLAlchemy 中正确地建立这种多对多关系?获取 "Bidirectional attribute conflict detected"

How do I properly do this many-to-many relationship in SQLAlchemy? Getting "Bidirectional attribute conflict detected"

我正在用代码设置我的数据库,但在尝试弄清楚如何按照我想要的方式建立多对多关系时遇到了麻烦。

基本上,我有一个 User class,它有一个带有 Team Class 的 m2m。确实,一个团队有很多用户,一个用户可以是多个团队的一部分。到目前为止一切都很好。当我想将 "admin" 权限授予团队的用户时,问题就出现了。

我尝试使用 Association Object,但我不知道如何将关系标记为管理员。因此,我创建了第二个映射 table 以将团队映射到用户 table 中的管理员。如图所示:

Base = declarative_base()

# Many to Many relationship mapping tables
user_team_map = Table(
    'user_team_map', Base.metadata,
    Column('user_id', Integer, ForeignKey('users.id'), primary_key=True),
    Column('team_id', Integer, ForeignKey('teams.id'), primary_key=True)
)

admin_team_map = Table(
    'admin_team_map', Base.metadata,
    Column('user_id', Integer, ForeignKey('users.id'), primary_key=True),
    Column('team_id', Integer, ForeignKey('teams.id'), primary_key=True)
)

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    first_name = Column(String(24), nullable=False)
    teams = relationship('Team', secondary=user_team_map, back_populates='users')
    admin_of = relationship('Team', secondary=admin_team_map, back_populates='users')


class Team(Base):
    __tablename__ = 'teams'
    id = Column(Integer, primary_key=True)
    name = Column(String(100), nullable=False)
    users = relationship('User', secondary=user_team_map, back_populates='teams')
    admins = relationship('User', secondary=admin_team_map, back_populates='teams')

当我尝试创建这些 table 时,我得到: ValueError: Bidirectional attribute conflict detected: Passing object <User at 0x7f2089d7dcc0> to attribute "Team.admins" triggers a modify event on attribute "Team.users" via the backref "User.teams".

我当然不是数据库向导,所以我想这是我的整体方法不正确。我应该如何去实现我想要的关系?如果我应该使用关联对象,我该怎么做?

Qualification
I don't know SQLAlchemy, so I cannot answer in those terms. I will answer in Relational-Database and SQL terms: I am sure you can figure out the implementation. The terms I use are Relational only.

是的,错误消息是有效的,即使错误消息文本可能不是特定的或未指示您的问题,因为您确实有重复项(数据库或 ORM 中不接受 table ).例如。根据资格,我不会使用术语“*定向”,因为关系在关系数据库或 SQL(它们有父子)中没有方向。

这个问题有两个层次,我会按逻辑顺序解释。第一层是数据库存储,第二层是将存储转换成Classes.

的问题

1 个存储空间

1.1 关联 Table

首先需要了解物理层面。尽管它通常是逻辑设计的结果,但似乎那里存在差距:您在没有正式考虑逻辑的情况下就进入了物理。

您拥有的是关联 table user_team_map。它将 usersteams 相关联,并将 n 对 n 关系解析为两个 0 对 n 关系,每个父对象一个。 (我也不会使用 map 这个词。)

  • 我所有的数据模型都在 IDEF1X 中呈现,这是自 1993 年以来的关系数据库建模标准。
  • 我的IDEF1X Introduction是必读的。

1.2 你的,关系化的

那么你需要确定的是:

each team has 0-to-n has admins(即users

但是您“无法弄清楚如何将关系标记为管理员”,因此您将整个 user_team_map 结构复制到 admin_team_map

  • 这是一个严重的归一化错误,会导致逻辑和物理层面的问题。因此 SQLAlchemy 吐出来也就不足为奇了(尽管错误消息文本不是指示性的)。首先,它允许 user 成为 team 中的 admin 而不是 team 中的 users

我们只需要在 user_team-map table 中将 user“标记”为 admin。关联 table 包含 对两个父项的引用,向其添加一列会将其移出关联 table 类别,进入普通 table.

  • 空值是自杀。它们是由于规范化错误造成的。

1.3 关系解决方案

  • 请注意,这实现了谓词:
    each team has 0-to-n admins
    不是谓词:
    each team has 0-to-1 admin
  • MemberName & AdminNameUserName
  • 的角色名

1.4 记录归档系统解决方案(退化和混乱)

由于“理论家”和“作者”将 1960 年代之前的关系系统宣传和营销为“关系”,在“文献”和“教科书”中,存在很多混乱。生成的 RFS 具有 none 的完整性;力量;或关系系统的速度。无论如何,这就是 ORM 使用的,这就是您正在使用的,所以我将提供它。

如果遵循异教徒的指示,这就是所谓的“关系”

  • 关系模型中禁止的重复行未被阻止
  • 庸人喜欢他们的重复 Foreign Key 引用,以及由此引起的循环引用问题。

1.5 记录归档系统解决方案(退化)

如果我们只修复这两个错误。

  • 请注意,物理指针 Record Id.
  • 在每个 table 中需要一个额外的列和一个额外的索引
  • 无法删除正确的索引(例如 UserName):如果您这样做 [1.4],您将允许重复的行(不是记录),这是被禁止的。
  • 所有其他原始性尚未解决
  • 在这个简单的示例中,如果减去 Idiot Record Ids,它会提升到关系 [1.3]。

2 Class

您使用的 类 中似乎存在理解和执行错误。这是使用 ORM 时的经典且非常常见的做法。当底层存储没有被正确理解或实现时,问题就变得复杂了。

我相信你会实施 [1.5]:

  • 为了理智和易于实施(防止 preventable 错误),基础 Classes 必须与数据库中的存储相匹配:这些是原子的,因为你会 CRUD 他们
  • 对于需要原子 Class 和倍数值(列表)的 windows,您需要一个单独的 Class,它是只读的(您不应该通过更新数据库这些对象)。

再来一个

如果您想了解更多关于关系的信息,以及它们是如何在 SQL 级别(包含逻辑级别的所有可能性)实现的,请阅读此 this Answer(第一部分仅)。