Python SQLAlchemy:如何在数据库中找到二级关系?

Python SQLAlchemy:如何在数据库中找到二级关系?,python,permissions,sqlalchemy,flask,flask-sqlalchemy,Python,Permissions,Sqlalchemy,Flask,Flask Sqlalchemy,我正在为我的Flask应用程序编写一个权限系统,但在确定如何在数据库中查找关系时遇到了问题,如果你想看到一切。装饰器旨在限制对装饰视图的访问 def user_has(attribute): """ Takes an attribute (a string name of either a role or an ability) and returns the function if the user has that attribute """ def wrap

我正在为我的Flask应用程序编写一个权限系统,但在确定如何在数据库中查找关系时遇到了问题,如果你想看到一切。装饰器旨在限制对装饰视图的访问

def user_has(attribute):
    """
    Takes an attribute (a string name of either a role or an ability) and returns the function if the user has that attribute
    """
    def wrapper(func):
        @wraps(func)
        def inner(*args, **kwargs):
            attribute_object = Role.query.filter_by(name=attribute).first() or \
                Ability.query.filter_by(name=attribute).first()

            if attribute_object in current_user.roles or attribute in current_user.roles.abilities.all():
                return func(*args, **kwargs)
            else:
                # Make this do someting way better.
                return "You do not have access"
        return inner
    return wrapper
我正在使用SQLAlchemy并在数据库中存储用户、角色和能力。用户可以有一个或多个角色。角色可以有一个或多个能力。我想获取传递给decorator的字符串,并检查用户是否具有该角色,或者用户的某个角色是否具有该能力。decorator不应该关心是否使用角色或能力参数调用它

显然,这种方法(
current_user.roles.abilities.all()
)无法像我在这里试图查找
abilities
那样通过我的关系数据库。我收到一条错误消息:

AttributeError: 'InstrumentedList' object has no attribute 'abilities'
如何将字符串参数与当前用户从其角色中获得的能力进行比较

作为参考,我的模型:

user_role_table = db.Table('user_role',
                           db.Column(
                               'user_id', db.Integer, db.ForeignKey('user.uid')),
                           db.Column(
                           'role_id', db.Integer, db.ForeignKey('role.id'))
                           )

role_ability_table = db.Table('role_ability',
                              db.Column(
                                  'role_id', db.Integer, db.ForeignKey('role.id')),
                              db.Column(
                              'ability_id', db.Integer, db.ForeignKey('ability.id'))
                              )


class Role(db.Model):
    __tablename__ = 'role'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(120), unique=True)
    abilities = db.relationship(
        'Ability', secondary=role_ability_table, backref='roles')

    def __init__(self, name):
        self.name = name.lower()

    def __repr__(self):
        return '<Role {}>'.format(self.name)

    def __str__(self):
        return self.name


class Ability(db.Model):
    __tablename__ = 'ability'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(120), unique=True)

    def __init__(self, name):
        self.name = name.lower()

    def __repr__(self):
        return '<Ability {}>'.format(self.name)

    def __str__(self):
        return self.name


class User(db.Model):
    __tablename__ = 'user'
    uid = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), unique=True)
    pwdhash = db.Column(db.String(100))
    roles = db.relationship('Role', secondary=user_role_table, backref='users')

    def __init__(self, email, password, roles=None):
        self.email = email.lower()

        # If only a string is passed for roles, convert it to a list containing
        # that string
        if roles and isinstance(roles, basestring):
            roles = [roles]

        # If a sequence is passed for roles (or if roles has been converted to
        # a sequence), fetch the corresponding database objects and make a list
        # of those.
        if roles and is_sequence(roles):
            role_list = []
            for role in roles:
                role_list.appen(Role.query.filter_by(name=role).first())
            self.roles = role_list
        # Otherwise, assign the default 'user' role. Create that role if it
        # doesn't exist.
        else:
            r = Role.query.filter_by(name='user').first()
            if not r:
                r = Role('user')
                db.session.add(r)
                db.session.commit()
            self.roles = [r]

        self.set_password(password)

    def set_password(self, password):
        self.pwdhash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.pwdhash, password)

    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return unicode(self.uid)

    def __repr__(self):
        return '<User {}>'.format(self.email)

    def __str__(self):
        return self.email

有没有可能因为在if语句的第二个子句中使用了
attribute
而不是
attribute\u object
,所以这不起作用

与此相反:

 if attribute_object in current_user.roles or attribute in current_user.roles.abilities.all():
试试这个:

 if attribute_object in current_user.roles or attribute_object in current_user.roles.abilities.all():

有没有可能因为在if语句的第二个子句中使用了
attribute
而不是
attribute\u object
,所以这不起作用

与此相反:

 if attribute_object in current_user.roles or attribute in current_user.roles.abilities.all():
试试这个:

 if attribute_object in current_user.roles or attribute_object in current_user.roles.abilities.all():

解决方案最终变得非常简单。我觉得从一开始就不知道有点傻

user_abilities = []
for role in current_user.roles:
    user_abilities += [role.ability for ability in role.abilities]

我仍然觉得可能有更好的解决方案,但解决方案毫无问题。

解决方案最终变得非常简单。我觉得从一开始就不知道有点傻

user_abilities = []
for role in current_user.roles:
    user_abilities += [role.ability for ability in role.abilities]

我仍然觉得可能有更好的模式,但解决方案可以顺利运行。

这是一个好问题。我已经解决了,但是我仍然有以前遇到的问题<代码>属性错误:“InstrumentedList”对象没有属性“能力”它告诉我,这不是访问与用户角色相关的能力的方法,但我不知道正确的方法。这是一个好问题。我已经解决了,但是我仍然有以前遇到的问题<代码>属性错误:“InstrumentedList”对象没有属性“能力”它告诉我,这不是访问与用户角色相关的能力的方法,但我不知道正确的方法。