2013-08-20 2 views
0

Я пишу систему разрешений для своего приложения Flask, и у меня возникают проблемы с выяснением того, как найти отношения в базе данных. My github repo, если вы хотите все увидеть. Декоратор предназначен для ограничения доступа к декорированным видам.SQLAlchemy: Как найти отношения второй степени в базе данных?

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 и сохраняю пользователей, роли и способности в базе данных. Пользователи могут иметь одну или несколько ролей. Роли могут иметь одну или несколько способностей. Я хочу передать строку, переданную декоратору, и проверить, имеет ли пользователь эту роль, или если одна из ролей пользователя обладает этой способностью. Декоратору все равно, будет ли он вызван с аргументом роли или способности.

По-видимому, этот метод (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 

и декорированный вид:

@app.route('/admin', methods=['GET', 'POST']) 
@user_has('admin') 
def admin(): 
    users = models.User.query.all() 
    forms = {user.uid: RoleForm(uid=user.uid, roles=[role.id for role in user.roles]) 
      for user in users} 

    if request.method == "POST": 
     current_form = forms[int(request.form['uid'])] 

     if current_form.validate(): 
      u = models.User.query.get(current_form.uid.data) 
      u.roles = [models.Role.query.get(role) 
         for role in current_form.roles.data] 
      db.session.commit() 
      flash('Roles updated for {}'.format(u)) 

    return render_template('admin.html', users=users, forms=forms) 

ответ

2

Решение в конечном итоге мертв простой. Я чувствую себя немного глупо, не зная об этом с самого начала.

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

Я по-прежнему чувствую, что для этого, вероятно, лучший образец, но решение работает без заминки.

1

Любой шанс это не работает, потому что вы используете attribute вместо attribute_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(): 
+0

Это хороший улов. Я исправил это, но у меня все еще есть проблема, с которой я сталкивался раньше. Объект AttributeError: «InstrumentedList» не имеет атрибутов «способности». Он говорит мне, что это не способ получить доступ к возможностям, связанным с ролями пользователя, но я не знаю правильного пути. – raddevon

Смежные вопросы