1. 扩展初始化
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy(app)
2. 数据模型关系
假设我们要为一个博客系统定义关系模型。
首先,系统中要有文章和用户:
# 博客文章
class Post(db.Model):
__tablename__ = 'post' #定义表名,表明默认为类名小写,所以本行删去也可以
id = db.Column(db.Integer, primary_key=True) #定义主键
body = db.Column(db.Text)
#用户
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True) #定义长度和其他参数
(db.Model 是一个基类,具有__tablename__, query 等属性。)
如何体现用户和文章之间的关系呢?假设一篇文章只有由一名用户编写,一名用户可以编写多篇文章。这是一个典型的一对多关系。定义这样的关系需要两步。
1. 在多侧定义外键
class Post(db.Model):
...
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
# 外键 = db.Column(字段类型, db.ForeignKey( 另一侧表名. 主键字段名)
2. 在出发侧(” 一侧 ”)定义关系属性
class User(db.Model):
...
posts = db.relationship('Post')
#集和关系属性 = db.relationship('另一侧类名') #这里使用类名,表明返回对象的类型
这样,调用某个 User 对象的 posts 关系属性,就能得到一个与之相对应的 Post 对象列表,其 author_id 值与这个 User 对象的 id 值相等。
同理,在 Post 对象中,也可以定义类似的 author 属性。这样,可以获取一篇博客文章对应的作者,也可以获取一名用户的多篇文章了。在 Post 类中添加相应的关系字段:
class Post(db.Model):
...
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
author = db.relationship('User')
似乎这样就行了,但是这样做的问题在于修改其中一个对象实例的字段并不会自动更新另一个类型对应的字段。例如:
>>> user = User()
>>> post = Post()
>>> post.author = user
>>> print(user.posts)
[]
尽管更新了 post 的 author 字段,user 的 posts 字段依然为空,而在逻辑上,用户的文章字段和文章的作者字段是彼此关连的,在其中一方更新时,另一方应该同时更新为好。这是一个双向的关系,使用 back_populates 参数就可以实现:
class User(db.Model):
...
posts = db.relationship('Post', back_populates='author')
class Post(db.Model):
...
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
author = db.relationship('User', db.ForeignKey('posts'))
#集和关系属性 = db.relationship('另一侧类名', back_populates='另一侧相关联的属性名'
使用 backref 参数可以实现同样的效果,但是更加简化:
class User(db.Model):
...
posts = db.relationship('Post', backref='author')
#集和关系属性 = db.relationship('另一侧类名',backref='在另一侧自动生成的相关联的属性名')
class Post(db.Model):
...
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
#此处 Post 已具备 author 属性
不过本着“显式优于隐式”,还是使用 back_populates 为宜。
3. 级联
cascade,在操作一个对象时对相关的对象也采取某些操作。在博客系统中,博客文章与文章评论就是一个例子。单独存在的一条评论是没有意义的。文章评论(子)关联并依赖于博客文章(父),在文章被添加到数据库时,相关评论也被添加;文章被删除时,评论也没有存在于数据库的必要了;若评论与文章解除了关联,成为了孤立对象(orphan object(孤儿对象 2333)),评论也应被删除。
级联行为通过关系函数 relationship() 的 cascade 参数设置。上述三种情况分别对应 save-update,delete 和 delete-orphan 参数值。参数值可组合。其余参数值及其详细解释可见 Sqlalchemy 文档:cascades
class Post(db.Model):
...
comments = db.relationship('Comment',back_populates='post',
cascade="all, delete-orphan")
#all = save-update + merge + delete
class Comments(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text)
post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
post = db.relationship('Post', back_populates='comments')