使用关联对象模式的 SQLAlchemy 惰性 = 动态与 m2m 关系

ste*_*ldo 5 python sqlalchemy flask flask-sqlalchemy

users我和表之间有一个简单的 m2m 关系roles

users_roles = db.Table('users_roles',
    db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
    db.Column('role_id', db.Integer, db.ForeignKey('roles.id')),
    db.Column('is_primary', db.Boolean)
)

class User(db.Model):
    __tablename__ = 'users'
    id = db.Column('id', db.Integer, primary_key=True)
    roles = db.relationship('Role', secondary=users_roles, lazy='dynamic', backref=db.backref('users', lazy='dynamic'))

class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column('id', db.Integer, primary_key=True)
    users = db.relationship('User', secondary=users_roles, lazy='dynamic', backref=db.backref('roles', lazy='dynamic'))
Run Code Online (Sandbox Code Playgroud)

要将记录添加到users_roles表中,我必须执行以下操作:

role = Role.get(1)
user = User()
user.roles.append(role)
db.session.add(user)
db.session.commit()
Run Code Online (Sandbox Code Playgroud)

没关系,但我is_primaryusers_roles表中有一列也应该填充。

我更改了代码以使用关联对象模式,如 SQLAlchemy 文档中所述。

现在我的代码如下所示:

class User(db.Model):
    __tablename__ = 'users'
    id = db.Column('id', db.Integer, primary_key=True)

class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column('id', db.Integer, primary_key=True)

class UserRole(db.Model):
    __tablename__ = 'users_roles'
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'), primary_key=True)
    is_primary = db.Column(db.Boolean)

    user = db.relationship(User, backref="users_roles")
    role = db.relationship(Role, backref="users_roles")

User.roles = association_proxy("users_roles", "role")
Role.users = association_proxy("users_roles", "user")
Run Code Online (Sandbox Code Playgroud)

它工作得很好,但我仍然有一个问题。User.roles(使用关联代理添加)是否有可能返回一个AppenderBaseQuery,我可以添加更多过滤器,例如 User.query.get(1).roles.filter_by(...) ?我过去常常在关系声明中使用lazy=dynamic来使用简单的多对多关系来做到这一点,但是在将类映射到关联表之后,我似乎无法再这样做了。有办法实现吗?

@IfLoop 我在这篇文章中遵循了你的建议。我们将非常感谢您的帮助。

ste*_*ldo 3

好吧,我最终roles使用以下代码进行过滤:

roles = Role.query.filter_by(...).join(UserRole).join(User).filter_by(id=1)
Run Code Online (Sandbox Code Playgroud)

我仍然希望能够做这样的事情:

roles = User.query.get(1).roles.filter_by(...).all()
Run Code Online (Sandbox Code Playgroud)

无论如何,如果我在几天内没有得到答案,我会接受这个答案。