0

我正在尝试对系统具有用户、公司、组和角色模型的情况进行建模。其中关系如下。

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    email = Column(Unicode(50), unique=True, nullable=False)
    first_name = Column(Unicode(50), nullable=False)
    last_name = Column(Unicode(50), nullable=False)
    company_id = Column(Integer, ForeignKey('companies.id'))

class Company(Base):
    __tablename__ = 'companies'
    id = Column(Integer, primary_key=True)
    name = Column(Unicode(200), unique=True, nullable=False)

    users = relationship("User", backref="company")
    groups = relationship("Group", backref="company")

class Group(Base):
    __tablename__ = 'groups'
    id = Column(Integer, primary_key=True)
    name = Column(Unicode(60), nullable=False)
    company_id = Column(Integer, ForeignKey('companies.id'), nullable=False)
    roles = relationship("Role", backref="group")

class Role(Base):
    __tablename__ = 'roles'
    id = Column(Integer, primary_key=True)
    name = Column(Unicode(60), nullable=False)
    group_id = Column(Integer, ForeignKey('groups.id'), nullable=False)

我的问题是,我不确定如何强制执行约束,即用户不能成为 Group 或 Role 的成员,除非是拥有 Group 的公司的成员。

4

1 回答 1

0

1)回答评论中的问题:
以您的模型为起点,我将简单地添加M-N用户和角色之间的关系以完成关系模型:

class UserRole(Base):
    __tablename__ = 'user_role'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
    role_id = Column(Integer, ForeignKey('roles.id'), nullable=False)

class User(Base):
    # ...
    roles = relationship("Role", secondary=UserRole.__table__, backref="users")

2)回答约束问题:

简单的答案:在当前模型的数据库级别上,您无法强制执行它(我不知道 RDBMS 提供了除简单 FK 之外的跨表约束工具)。但是,使用一些冗余数据,您可以这样做:

  1. 也存放company_idRole桌子上;添加复合 FK 指向Group
  2. group_id也存储company_iduser_role表中,并将复合 FK 添加到 中Role这基本上负责您所要求的内容

修改后的代码:

class Role(Base):
    __tablename__ = 'roles'
    id = Column(Integer, primary_key=True)
    name = Column(Unicode(60), nullable=False)
    group_id = Column(Integer, ForeignKey('groups.id'), nullable=False)
    company_id = Column(Integer, ForeignKey('companies.id'), nullable=False)
    __table_args__ = (ForeignKeyConstraint([company_id, group_id], [Group.company_id, Group.id]), {})

class UserRole(Base):
    __tablename__ = 'user_role'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
    group_id = Column(Integer, ForeignKey('groups.id'), nullable=False)
    company_id = Column(Integer, ForeignKey('companies.id'), nullable=False)
    role_id = Column(Integer, ForeignKey('roles.id'), nullable=False)
    __table_args__ = (ForeignKeyConstraint([company_id, group_id, role_id], [Role.company_id, Role.group_id, Role.id]), )

但这还不够,您必须修改更多代码,因为不同表之间有多个 FK,您需要显式配置关系。此外,您还需要添加一些代码(可能使用 ORM 事件)来自动添加父级的父级,等等。

3)解决方案?

您绝对可以使用 SA属性事件来验证您的模型:

from sqlalchemy import event
def append_user_role(target, value, initiator):
    assert target.company, "Cannot validate until the company is set"
    assert target.company == value.group.company#, "Cannot add person %s (from company %s) to a Role %s (from company %s)" % (target, target.company, value, value.group.company)
event.listen(User.roles, 'append', append_user_role)

我可能会将第3 点)与一些数据库视图结合起来,这些视图返回所有user_role无效的行以便清理那些不是通过 SA 插入的,而是通过其他方式(数据导入脚本等)

于 2012-06-28T11:56:05.193 回答