Flaskday07

Model 相关 & 一对多、多对多关系

目录

模型

先分析,再建立关系。这里使用了 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,只是一般不这么做)

  1. 模型,如果想建立外键,一定是在 “多” 的这一方设置一个字段用 ForeignKey 对应到 “一” 中的唯一主键中,这样就建立了一对多关系,需要注意的是,这是给数据库看的,告诉数据库“我已经建立了外键关系”;
  2. 然后就可以在中设置 relationship 来对应。而 relationship 不是给数据库看的,relationship() 函数的作用 要是在 view 和 template 中体现的,也就是给我们写逻辑使用的,即建立起开发人员可以操作的关系
  3. 那么具体到上面的例子,因为一个作者对应多篇文章,则我先在文章类中使用 ForeignKey,再在用户类中使用 relationship,具体代码如下。
  4. 关于参数
    • 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 字段放进去。 但是与一对多关系稍有不同:

下面是一个具体实例:

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'))

另外,有两点需要另外说明:

  1. secondary 可以接受 a callable that returns the ultimate argument,which is evaluated only when mappers are first used. ,即接受可执行参数,在上面的例子中,可以让 Page_tag 在稍晚的时候定义,甚至可以在所有模块都初始化完成后,直到它可调用为止;
  2. 删除多对多的记录时,不必手动删除 secondary 所绑定的中间表数据,数据库会根据 cascade rule 级联规则自动删除。
  1. 只要符合逻辑,比如用户发表了文章,文章被用户发表。

Some rights reserved
Except where otherwise noted, content on this page is licensed under a Creative Commons Attribution-NonCommercial 4.0 International license.