【问题标题】:Flask SQL-Alchemy creating hierarchy table many foreign keys to same tableFlask SQLAlchemy创建层次表许多外键到同一个表
【发布时间】:2020-07-17 06:29:42
【问题描述】:

有一个角色表:

from datetime import datetime
# from models.user_role import UserRoleModel
from models.role_hierarchy import RoleHierarchyModel


class RoleModel(db.Model):
    __tablename__ = "security_role"
    role_id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(255))
    description = db.Column(db.Text)
    created_timestamp = db.Column(db.DateTime, default=datetime.now())
    updated_timestamp = db.Column(db.DateTime, default=datetime.now())

    role = db.relationship('UserRoleModel', back_populates='role')

    parent_role = db.relationship('RoleHierarchyModel',
                                  back_populates='parent_role',
                                  primaryjoin="RoleHierarchyModel.parent_role_id == RoleModel.role_id")
    child_role = db.relationship('RoleHierarchyModel',
                                 back_populates='child_role',
                                 primaryjoin="RoleHierarchyModel.child_role_id == RoleModel.role_id")

和角色层次结构表

class RoleHierarchyModel(db.Model):
    __tablename__ = "security_role_hierarchy"
    role_h_id = db.Column(db.Integer, primary_key=True)
    parent_role_id = db.Column(db.Integer, db.ForeignKey('security_role.role_id'))
    child_role_id = db.Column(db.Integer, db.ForeignKey('security_role.role_id'))
    created_at = db.Column(db.DateTime, default=datetime.now())
    updated_at = db.Column(db.DateTime, default=datetime.now())

    parent_role = db.relationship('RoleModel', back_populates='parent_role'
                                  , foreign_keys='RoleHierarchyModel.parent_role_id')
    child_role = db.relationship('RoleModel', back_populates='child_role'
                                 , foreign_keys='RoleHierarchyModel.child_role_id')

不确定我是否已正确设置,也不确定如何在完成此操作后将其正确输入数据库。我要创建的是一个名为超级用户的角色,该角色具有与之关联的其他角色

我试试:

base_role = RoleModel(title='Read')
other_base_role = RoleModel(title='Write')

base_role.save_to_db()
other_base_role.save_to_db()

super_user = RoleModel(title='Super)
hierarchy1 = RoleHierarchyModel()
hierarchy2 = RoleHierarchyModel()

hierarchy1.child_role(base_role)
hierarchy2.child_role(other_base_role)

super_user.parent_role.append(hierarchy1)
super_user.parent_role.append(hierarchy2)

但我不断收到以下错误:

sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original
exception was: Attempting to flush an item of type <class 'models.role.RoleModel'> as a member of collection "RoleModel.child_role". Expected an object of type <class 'models.role_hierarchy.RoleHierarchyModel'> or
 a polymorphic subclass of this type. (Background on this error at: http://sqlalche.me/e/7s2a)

我做错了什么?

【问题讨论】:

  • 首先,您只允许一个子角色和一个父角色,而您似乎希望有多个子角色和一个父角色。这样,您会得到一个树形结构,其中一个角色拥有它所有子角色的权利,也许还有一些额外的权利只属于他们自己,对吧?
  • 是的,就是这样!我将如何指定?

标签: python flask-sqlalchemy


【解决方案1】:

一般来说,不需要有两个类,这就是你的问题开始的地方。

要使它成为一棵合适的树,请查看this answer。您只需通过列引用 parent_id,并让 SQLAlchemy 通过relationship 对象获取子项。

假设您以某种方式将角色拥有的权限存储在列表中。然后,如果您想查看某人是否有权做某事,或者您想返回一个人(直接和间接)拥有的所有权利的列表:

class Role:
   _rights = ['canChangePassword', 'canEditPosts', 'canDeletePosts'...]

   def has_right(self, rightName):
       if rightName in self._rights
           return True
       for child in self.children:
           if child.has_right(rightName):
               return True
       return False

   def get_rights(self):
       all_rights = self._rights
       for child in self.children:
           all_rights += self.children.get_rights()
       # there can be duplicates, cast to a set to remove them
       return list(set(all_rights))

我还可以看到如何根据描述或标题授予权限,但在这种情况下,只需以相同的方式迭代并从其自身和所有子级构建角色标题列表。在这里使用递归来发挥你的优势。

【讨论】:

    【解决方案2】:

    找到隐藏在 SQLAlchemy 文档中的答案:https://docs.sqlalchemy.org/en/13/orm/join_conditions.html#handling-multiple-join-paths

    https://docs.sqlalchemy.org/en/13/orm/join_conditions.html#handling-multiple-join-paths

    角色表现在看起来像:

    class RoleModel(db.Model):
        __tablename__ = "security_role"
        role_id = db.Column(db.Integer, primary_key=True)
        title = db.Column(db.String(255))
        description = db.Column(db.Text)
        created_timestamp = db.Column(db.DateTime, default=datetime.now())
        updated_timestamp = db.Column(db.DateTime, default=datetime.now())
    
        role = db.relationship('UserRoleModel', back_populates='role')
    
        child_roles = db.relationship('RoleModel', secondary='security_role_hierarchy'
                                      , primaryjoin='RoleModel.role_id==RoleHierarchyModel.parent_role_id'
                                      , secondaryjoin="RoleModel.role_id==RoleHierarchyModel.child_role_id",
                                      backref="parent_roles")
    

    层次结构表现在是:

    class RoleHierarchyModel(db.Model):
        __tablename__ = "security_role_hierarchy"
        role_h_id = db.Column(db.Integer, primary_key=True)
        parent_role_id = db.Column(db.Integer, db.ForeignKey('security_role.role_id'))
        child_role_id = db.Column(db.Integer, db.ForeignKey('security_role.role_id'))
        created_at = db.Column(db.DateTime, default=datetime.now())
        updated_at = db.Column(db.DateTime, default=datetime.now())
    

    【讨论】:

      猜你喜欢
      • 2013-06-03
      • 2019-05-22
      • 2015-02-04
      • 1970-01-01
      • 2016-02-05
      • 2019-11-24
      • 2017-11-10
      • 2016-12-23
      • 1970-01-01
      相关资源
      最近更新 更多