模型
先分析,再建立关系。这里使用了 Flask-SQLAlchemy 扩展,也可参照 Flask-SQLAlchemy 详解 服用。
一对多
假设存在作者(User)和文章(Article)两个类,一个作者可以对应多篇文章,形成 一对多的关系。(这里假设不存在联合作者的情况,即不是多对多的关系)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True,autoincrement=True)
username = db.Column(db.String(15),nullable=False)
password = db.Column(db.String(64),nullable=False)
class Article(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(50), nullable=False)
content = db.Column(db.Text, nullable=False)
常规做法:一对多模型,先在多中使用 ForeignKey ,再在一中使用 relationship(当然你也可以在多中使用 relationship 1,只是一般不这么做)
- 一对多模型,如果想建立外键,一定是在 “多” 的这一方设置一个字段用 ForeignKey 对应到 “一” 中的唯一主键中,这样就建立了一对多关系,需要注意的是,这是给数据库看的,告诉数据库“我已经建立了外键关系”;
- 然后就可以在一中设置 relationship 来对应多。而 relationship 不是给数据库看的,
relationship()
函数的作用 要是在 view 和 template 中体现的,也就是给我们写逻辑使用的,即建立起开发人员可以操作的关系; - 那么具体到上面的例子,因为一个作者对应多篇文章,则我先在文章类中使用 ForeignKey,再在用户类中使用 relationship,具体代码如下。
- 关于参数
ForeignKey()
中 填写一对多模型中的一的唯一主键,参数为:引用的表表名.引用表的主键
, 因为是表名,所以为小写。relationship()
中通常有两个参数,第一个参数是该关系对应的模型类类名,不能随意起(所以为大写);第二个参数为backref
,是回调的引用,可以自定义名字,比如下方中的语句articles = db.relationship('Article, backref='user')
,表示作者类与文章类建立关系,代表用户发表了文章,同时加了backref
参数就可以进行反向引用。
class User(db.Model): | |
id = db.Column(db.Integer, primary_key=True,autoincrement=True) | |
username = db.Column(db.String(15),nullable=False) | |
password = db.Column(db.String(64),nullable=False) | |
articles = db.relationship('Article', backref='username') | |
class Article(db.Model): | |
id = db.Column(db.Integer, primary_key=True, autoincrement=True) | |
title = db.Column(db.String(50), nullable=False) | |
content = db.Column(db.Text, nullable=False) | |
user_id = db.Column(db.integer, db.ForeignKey('user.id')) | |
# user = db.realtionship('User', backref='articles') |
注:在一对多模型中,一中设置 relationship 但多中未设置 ForeignKey,系统将报错,也很容易理解:你都没有真正的在数据库中建立关系,我还怎么操作你?
所以经过上面的操作,我们进行如下操作:
user.articles # 获得该用户对应的所有文章
article.username # 反查该文章对应的用户,此处的 username 是在 backref 中定义的那个
多对多
实际上多对多,就是两个 one-to-many;两个表建立多对多关系时,通常会新建一个表,把两个表的 id 字段放进去。 但是与一对多关系稍有不同:
- 一对多关系中,在多中设置外键;可是在多对多中,两边都是多,通常采用新建一个表,然后在这个表中设置两个多的外键;
- 而 relationship 则在这两个表中的一个设置即可,除了用
relationship()
关联这两个表中的另一个表,还用到了secondary
参数绑定到这个新建的表;
下面是一个具体实例:
class Page(db.Model): | |
id = db.Column(db.Integer, primary_key=True) | |
tags = db.relationship('Tag', secondary='Page_tag', backref='pages') | |
class Tag(db.Model): | |
id = db.Column(db.Integer, primary_key=True) | |
name = db.Column(db.String(20), nullable=False) | |
class Page_tag(db.Model): | |
id = db.Column(db.Integer, primary_key=True) | |
tag_id = db.Column(db.Integer, db.ForeignKey('tag.id')) | |
page_id = db.Column(db.Integer, db.ForeignKey('page.id')) |
另外,有两点需要另外说明:
secondary
可以接受 a callable that returns the ultimate argument,which is evaluated only when mappers are first used. ,即接受可执行参数,在上面的例子中,可以让Page_tag
在稍晚的时候定义,甚至可以在所有模块都初始化完成后,直到它可调用为止;- 删除多对多的记录时,不必手动删除
secondary
所绑定的中间表数据,数据库会根据 cascade rule 级联规则自动删除。
只要符合逻辑,比如用户发表了文章,文章被用户发表。↩