在SQLAlchemy中查询多对多

时间:2014-08-19 20:52:31

标签: python sqlalchemy

我有以下型号。用户具有多个角色,角色可以拥有许多权限。我无法弄清楚如何查询以获得我想要的东西。

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

role_permission = db.Table(
    'role_permission', 
    db.Column('permission_id', db.Integer, db.ForeignKey('permission.id')),
    db.Column('role_id', db.Integer, db.ForeignKey('role.id')),
    db.UniqueConstraint('permission_id', 'role_id')
)

class Role(Base):
    __tablename__ = 'role'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), unique=True, nullable=False)

class Permission(Base):
    __tablename__ = 'permission'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)

    roles = db.relation(Role, secondary=role_permission, backref=db.backref('permissions'))

class User(Base, UserMixin):
    __tablename__ = 'user'

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(60), unique=True, nullable=False)
    password_hash = db.Column(db.String(80), nullable=False)

    roles = db.relation(Role, secondary=user_role, backref=db.backref('users'))

我想获得分配给用户的所有权限的列表(最好是唯一的),但我似乎无法弄清楚如何做到这一点。

我可以通过在用户模型中创建生成器来获取列表:

def get_all_permissions(self):
    for role in self.roles:
        for perm in role.permissions:
            yield perm

但我希望能够在一次查询中做到这一点。

3 个答案:

答案 0 :(得分:1)

好吧,要获取权限列表,请尝试以下方法:

permissions = session.query(Permission).\
    join(Role).join(User).filter(User.username='MisterX').all()

或过滤所需的任何内容。要使权限唯一,您可以使用group by:

permissions = session.query(Permission.id, Permission.name).join(Role).join(User).\
    filter(User.username='MisterX').group_by(Permission.id).all()

或者,如果没有特殊查询,请使用声明性扩展,如果可以:

permissions = User.roles.permissions

这有帮助吗?

答案 1 :(得分:1)

可能无法正确识别关联表,因为您未指定元数据参数。这个脚本适合我:

#!/bin/python

from sqlalchemy import Table
from sqlalchemy import Integer, String, ForeignKey, create_engine, Column, PrimaryKeyConstraint
from sqlalchemy.orm import relationship, backref, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base()

user_role = Table(
    'user_role',
    Base.metadata,
    Column('user_id', Integer, ForeignKey('users.id')),
    Column('role_id', Integer, ForeignKey('roles.id')),
    PrimaryKeyConstraint('user_id', 'role_id')
)

role_permission = Table(
    'role_permission', 
    Base.metadata,
    Column('permission_id', Integer, ForeignKey('permissions.id')),
    Column('role_id', Integer, ForeignKey('roles.id')),
    PrimaryKeyConstraint('permission_id', 'role_id')
)

class Role(Base):
    __tablename__ = 'roles'

    id = Column(Integer, primary_key=True)
    name = Column(String(100), unique=True, nullable=False)

class Permission(Base):
    __tablename__ = 'permissions'

    id = Column(Integer, primary_key=True)
    name = Column(String(100), nullable=False)

    roles = relationship("Role", secondary=role_permission, backref=backref('permissions'))

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    username = Column(String(60), unique=True, nullable=False)
    password_hash = Column(String(80), nullable=False)

    roles = relationship("Role", secondary=user_role, backref=backref('users'))


Base.metadata.create_all(engine)
session = sessionmaker(bind=engine)()

u = User(username="user", password_hash="secret")

r1 = Role(name="Role 1")
session.add(r1)
r2 = Role(name="Role 2")
session.add(r2)

p1 = Permission(name="Permission 1")
p2 = Permission(name="Permission 2")
p3 = Permission(name="Permission 3")

r1.permissions.append(p1)
r1.permissions.append(p2)

r2.permissions.append(p2)
r2.permissions.append(p3)


u.roles.append(r1)
u.roles.append(r2)

session.add(u)

for perm in session.query(Permission).join(Role, Permission.roles).\
    join(User, Role.users).filter(User.username=="user").distict()all():
    print(perm.name)

答案 2 :(得分:0)

如果您已将User对象与权限和角色一起加载到内存中,则代码应该快速执行,无需转到数据库。

否则,此查询应该有效:

    user_id = 789
    permissions = (db.session.query(Permission)
            .join(Role, Permission.roles)
            .join(User, Role.users)
            .filter(User.id == user_id)
            ).distinct()
    #print(permissions)
    for perm in permissions:
        print(perm)