Flask-SQLAlchemy 中的 relationship & backref

 

今天重看 Flask 时,发现对backref仍然没有理解透彻。查阅文档后发现,以前试图孤立地理解backref是问题之源,backref是与relationship配合使用的。

一对多关系

db.relationship()用于在两个表之间建立一对多关系。例如书中 roles 表中一个 User 角色,可以对应 users 表中多个实际的普通用户。实现这种关系时,要在“多”这一侧加入一个外键,指向“一”这一侧联接的记录。

class Role(db.Model):

# ...

users = db.relationship('User', backref='role')

class User(db.Model):

# ...

role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))

relationship & ForeighKey

大多数情况下, db.relationship() 都能自行找到关系中的外键, 但有时却无法决定把 哪一列作为外键。 例如, 如果 User 模型中有两个或以上的列定义为 Role 模型的外键, SQLAlchemy 就不知道该使用哪列。如果无法决定外键,你就要为 db.relationship() 提供额外参数,从而确定所用外键。(见书 P49)

relationship & backref

通过db.relationship(),Role 模型有了一个可以获得对应角色所有用户的属性users。默认是列表形式,lazy='dynamic'时返回的是一个 query 对象。即relationship提供了 Role 对 User 的访问。

backref正好相反,提供了 User 对 Role 的访问。

不妨设一个 Role 实例为 user_role,一个 User 实例为 u。relationship 使 user_role.users 可以访问所有符合角色的用户,而 backref 使 u.role 可以获得用户对应的角色。

示例



$ p manage.py shell

>>> user_role = Role.query.filter_by(name='User').all()

>>> user_role

[<Role u'User'>]

>>> user_role = Role.query.filter_by(name='User').first()

>>> user_role

<Role u'User'>

>>> user_role.users

<sqlalchemy.orm.dynamic.AppenderBaseQuery object at 0x1087c1050>

>>> user_role.users.order_by(User.username).all()

[<User u'alice78'>, <User u'andrea86'>, <User u'hmr'>]

>>> Role.query.all()

[<Role u'Moderator'>, <Role u'Administrator'>, <Role u'User'>]

>>> user_role.users.count()

3

>>> u = User.query.filter_by(username='hmr').first()

>>> u

<User u'hmr'>

>>> u.role

<Role u'User'>

一对一关系

除了一对多之外, 还有几种其他的关系类型。一对一关系可以用前面介绍的一对多关系表示, 但调用 db.relationship() 时要把 uselist 设为 False , 把“多”变成“一”。

多对多关系

如果你想要用多对多关系,你需要定义一个用于关系的辅助表。对于这个辅助表, 强烈建议  使用模型,而是采用一个实际的表:

tags = db.Table('tags',
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id')),
    db.Column('page_id', db.Integer, db.ForeignKey('page.id'))
)

class Page(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    tags = db.relationship('Tag', secondary=tags,
        backref=db.backref('pages', lazy='dynamic'))

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)

这里我们配置 Page.tags 加载后作为标签的列表,因为我们并不期望每页出现 太多的标签。而每个 tag 的页面列表( Tag.pages )是一个动态的反向引用。 正如上面提到的,这意味着你会得到一个可以发起 select 的查询对象。

猜你喜欢

转载自blog.csdn.net/mr_hui_/article/details/83217566