2012-06-28 67 views
0

我试图对系统具有用户,公司,组和角色模型的情况建模。关系如下。SQLAlchemy间表约束

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) 

我的问题是,我不知道我怎么可以强制用户无法成为集团成员或角色没有被拥有该集团的成员公司的约束。

+0

您的示例代码是否缺少模型中'Users'链接到Groups的部分? – van

+0

是的。我不确定最好的方法来做到这一点,所以我把它排除了。我假设向群组和角色添加users = relationship(“User”,backref =“group | role”)是正确的? –

回答

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_idcompany_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,你需要配置关系explici TLY。您还需要添加一些代码(可能使用ORM事件)自动添加父代的父代,等等。

3)解决方案?

你绝对可以使用SA Attribute Events来验证你的模型:

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) 

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

+0

谢谢你。事件似乎是解决这个问题的最好方法。我在组和角色上都使用了@validates('users')来确定在将用户添加到组/角色之前已满足约束条件,并且在用户使用'remove'清除组/角色时使用了一个事件公司或集团的变化。尽管我现在必须确保任何外部应用程序也能够满足这些条件。 –

+0

这听起来像是一个非常好的解决方案。验证在系统之外执行的更改始终是一项挑战。 – van