使用 Flask-SQLAlchemy表示实体间的关系
在数据库中,实体间的关系可以是一对一(1:1)、一对多(1:N)、多对多(M:N)或多对一(N:1)。
以下是使用 Flask-SQLAlchemy 表示这些关系的代码示例:
1. 一对一(1:1)关系
假设每个用户(User)有一个唯一的配置(Config),反之亦然。
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
# 其他字段...
config_id = db.Column(db.Integer, db.ForeignKey('configs.id'))
config = db.relationship('Config', uselist=False, backref=db.backref('user', uselist=False))
class Config(db.Model):
__tablename__ = 'configs'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
# 配置字段...
2. 一对多(1:N)关系
假设每个用户(User)可以有多个帖子(Post),但每个帖子只能属于一个用户。
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
posts = db.relationship('Post', backref='author', lazy=True)
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(35), nullable=False)
body = db.Column(db.String(200), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
3. 多对多(M:N)关系
假设用户(User)和组(Group)之间是多对多的关系,可以通过一个关联表(association table)来实现。
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
groups = db.relationship('Group', secondary='user_groups', backref=db.backref('users', lazy='dynamic'))
class Group(db.Model):
__tablename__ = 'groups'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=True, nullable=False)
# 关联表
user_groups = db.Table('user_groups',
db.Column('user_id', db.Integer, db.ForeignKey('users.id'), primary_key=True),
db.Column('group_id', db.Integer, db.ForeignKey('groups.id'), primary_key=True)
)
4. 多对一(N:1)关系
这与一对多(1:N)关系相反,但实现方式相同。以用户(User)和帖子(Post)为例,每个帖子属于一个用户。
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(35), nullable=False)
body = db.Column(db.String(200), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
在这些示例中,db.relationship 用于定义模型之间的关系,db.ForeignKey 用于定义外键约束,而 db.Table 用于定义多对多关系的关联表。backref 参数用于创建反向引用,lazy 参数控制加载关系的方式。
backref 参数
db.relationship 函数的 backref 参数用于在两个模型之间创建反向引用。
这允许我们从关系的另一端访问到原始模型的实例。反向引用可以是显式的,也可以是隐式的。
1. 显式反向引用
当我们使用 backref 参数时,我们可以指定一个字符串,这个字符串是你要在关联模型上创建的属性名。
这个属性将允许我们从关联模型访问到原始模型。
例如,如果我们有一个 User 模型和一个 Post 模型,每个用户可以写多个帖子,而每个帖子只能由一个用户编写,我们可以这样设置反向引用:
# User 模型有一个 posts 属性,它是一个 Post 对象的列表。
# 同时,每个 Post 对象都有一个 author 属性,它指向创建该帖子的 User 对象
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
posts = db.relationship('Post', backref='author', lazy=True)
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100))
content = db.Column(db.Text)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
2、隐式反向引用
如果我们在 db.relationship 中没有指定 backref 参数,SQLAlchemy 会尝试自动创建一个反向引用。
这个自动创建的反向引用属性名是原始模型名的小写形式加上 s(如果原始模型名已经是复数形式,则直接使用该名称)。
继续使用上面的 User 和 Post 模型的例子,如果我们不指定 backref 参数,那么 Post 模型将自动获得一个名为 users 的属性,指向 User 模型:
#Post 模型将有一个名为 users 的属性,这可能不是我们想要的,因为它可能会引起混淆
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
posts = db.relationship('Post', lazy=True)
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100))
content = db.Column(db.Text)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
使用 backref 的好处
使用 backref 可以:
- 提供更清晰的代码,因为我们可以自定义反向引用的属性名。
- 避免属性名冲突,特别是当模型名已经是复数形式时。
- 明确地定义关系的方向,使得代码更容易理解和维护。
注意事项
当我们使用 backref 时,我们可以指定额外的参数,如 lazy,来控制加载关联对象的方式。
如果我们不希望创建反向引用,可以传递 None 作为 backref 的值。
在定义 backref 时,确保不要与模型中的其他属性名冲突。
lazy 参数
在 Flask-SQLAlchemy 中,lazy 参数是 db.relationship 函数的一个选项,它决定了如何加载关联对象。
lazy 参数有以下几个选项:
1、lazy='select'(默认): 当你访问一个延迟加载的关系属性时,SQLAlchemy 会执行一个单独的 SELECT 语句来获取相关数据。这是最常用的选项,因为它允许你按需加载关系数据,而不是一次性加载所有数据。
2、lazy='joined': 当你访问一个延迟加载的关系属性时,SQLAlchemy 会尝试使用 JOIN 语句来一次性获取主表和关联表的数据。这种方式可以减少数据库查询次数,特别是当你需要同时访问主表和关联表的数据时。
3、lazy='subquery': 类似于 joined,但使用子查询来获取数据。这在你需要加载关联数据,并且关联表的数据量较大时很有用。
4、lazy='dynamic': 这种模式下,关系属性会返回一个可迭代的查询对象,而不是实际的数据。这意味着你可以对关系进行进一步的查询操作,如过滤、排序等。这种方式不适用于需要直接访问关系数据的场景。
5、lazy='immediate': 这种模式下,关系数据会在主数据加载时立即加载。这可能会导致大量的数据一次性被加载,特别是当关系表中的数据量很大时。
6、lazy=False: 这会禁用延迟加载,使得关系数据在主数据加载时立即加载。这与 lazy='immediate' 类似,但通常不推荐使用,因为它可能导致性能问题。
在实际应用中,选择哪种 lazy 模式取决于我们的具体需求:
如果我们通常只访问主表的数据,偶尔需要访问关联表的数据,那么 lazy='select' 是一个好选择。
如果我们经常需要同时访问主表和关联表的数据,那么 lazy='joined' 或 lazy='subquery' 可能更合适。
如果我们需要对关系数据进行复杂的查询操作,那么 lazy='dynamic' 可能是必要的。
如果我们确定在加载主数据时总是需要关联数据,并且不介意一次性加载所有数据,那么 lazy=False 或 lazy='immediate' 可能是合适的。
在设计数据库模型时,应该根据实际的业务逻辑和性能需求来选择最合适的 lazy 模式。

浙公网安备 33010602011771号