Skip to content

四、表关系

一、外键

外键通过db.ForeignKey实现

python
class Article(db.Model):
    __tablename__="article"
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    title=db.Column(db.String(200),nullable=False)
    content=db.Column(db.Text,nullable=False)
    # 添加作者的外键
    author_id=db.Column(db.Integer,db.ForeignKey("user.id"))

author_id因为引用user表的id字段,所以它的类型必须和user表的id字段一致

二、一对多关系

用户与文章就是一对多的关系

1、建立关系

python
class Article(db.Model):
    __tablename__="article"
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    title=db.Column(db.String(200),nullable=False)
    content=db.Column(db.Text,nullable=False)
    # 添加作者的外键
    author_id=db.Column(db.Integer,db.ForeignKey("user.id"))
    author=db.relationship("User")

2、建立双向关系

1、User模型

python
articles=db.relationship("Article",back_populates="author")

2、Article模型

 author=db.relationship("User",back_populates="articles")

3、简化关系定义

只需在一个模型上定义db.relationship,并且传递backref参数实现双向绑定

Article模型

 author=db.relationship("User",back_populates="articles")

三、一对一关系

用户表和用户拓展表是一对一关系,一个用户只能有一条拓展数据,一条拓展数据只能属于一个用户

设置uselist为Flase,即可把”多“设置为“一”

1、User模型

python
extension=db.relationship("UserExtension",back_populates="user",uselist=False)

2、UserExtension模型

python
user_id=db.Column(db.Integer,db.ForeignKey("user.id"),unique=True)
user=db.relationship("User",back_populates="extension")

四、多对多关系 未实验

多对多关系在数据库层面是需要通过一张中间表来实现的,在Flask-SQLAlchemy 中也是一样。

以文章和标签为例,一篇文章可以添加多个标签,一个标签可以被多篇文章添加

1、中间表

python
article_tag_table = db.Table(
	"article tag table",
	db.Column("article_id",db.Integer,db.ForeignKey("article.id"),primary_key=True),
	db.Column("tag_id",db.Integer,db.ForeignKey("tag.id"),primary_key=True)
)

2、Article

python
class Article(db.Model):
	tablename= "article"
	id = db.Column(db.Integer,primary_key=True,autoincrement=True)
	title =db.Column(db.string(200),nullable=False)
	content=db.Column(db.Text,nullable=False)
	
	author_id= db.Column(db.Integer,db.ForeignKey("user.id"))
	author=db.relationship("User",backref="articles")
	tags=db.relationship("Tag",secondary=article_tag_table,back_populates="articles")

3、Tag

python
class Tag(db.Model):
    __tablename__= "tag"
	id = db.Column(db.Integer,primary_key=True,autoincrement=True)
	name=db.Column(db.String(100))
	articles = db.relationship("Article",secondary=article_tag_table,back_populates="tags")

4、例子

python
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

# 定义 Tag 模型
class Tag(db.Model):
    __tablename__= "tag"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(100))
    articles = db.relationship("Article", secondary="article_tag_table", back_populates="tags")

# 定义 Article 模型
class Article(db.Model):
    __tablename__ = "article"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(200), nullable=False)
    content = db.Column(db.Text, nullable=False)
    author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
    author = db.relationship("User", backref="articles")
    tags = db.relationship("Tag", secondary="article_tag_table", back_populates="articles")

# 创建多对多关系的中间表
article_tag_table = db.Table(
    "article_tag_table",
    db.Column("article_id", db.Integer, db.ForeignKey("article.id"), primary_key=True),
    db.Column("tag_id", db.Integer, db.ForeignKey("tag.id"), primary_key=True)
)

五、级联操作未实验

级联操作(cascade)是在操作某个对象时,相关联的对象也会进行对应的操作。在数据库层面的级联操作包括级联删除、级联更新等

定义级联操作是通过对db.relationship传递cascade 参数实现的,这个参数的值可以为 allsave-updatemergerefesh-expireexpungedelete 中的一个或者多个,如果是多个,则通过英文逗号隔开,如“save-update,delete”。

谁设置了 cascade参数,谁就是父表,父表数据发生变化,相关联的从表也会执行相应操作。

1、Category

python
class Category(db.Model):
	__tablename__= "category"
	id = db.Column(db.Integer, primary_key=True, autoincrement=True)
	name=db.Column(db.String(100))
	newses = db.relationship("News",back_populates="category")

2、News

python
class News(db.Model):
	__tablename__= "news"
	id = db.Column (db.Integer,primary_key=True, autoincrement=True)
	title = db.Column(db.String(100))
	content=db.Column(db.Text)
	category_id= db.Column (db.Integer,db.ForeignKey("category.id"))
	category=db.relationship("Category" ,back_populates="newses")

1、save-update

save-update 是默认选项,它的作用是当某个对象被添加到会话中时,与此对象相关的对象也会被添加进去

python
category = Category(name="军事")
news = News(title="新闻1",content="新闻内容1")
news.category= category
db.session.add(news)
db.session.commit()

2、delete

delete 表示当删除某个对象时,被关联的所有对象都会被删除。这个值默认在 cascade中是没有

python
news = News.query.first()
db.session.delete(news)
db.session.commit()
return "success"

3、delete-orphan

1、 Category

python
class Category(db.Model):
newses = db.relationship("News",back_populates="category",cascade="delete,delete-orphan")

2、执行删除操作

python
category = Category.query.first()
news=News(title="新闻2",content="新闻内容2")
Category.newses.append(news)
db.session.commit()

# 将news从 category 中解除关联
category.newses.remove(news)
db.session.commit()

解释delete-orphan

首先将 news 添加到 category.newses 中,然后通过 commit 操作提交到数据库中。

接着从 category.newses 中移除,这样就把newscategory 上解除了关联

因为在 Category 中定义 newses 属性时,设置了 cascadedelete-orphan,那么一旦解除关联,news对象就成为孤儿(orphan)对象,即会自动从数据库中被删除。

这个选项一般用在一对多关系上,不能用在多对多以及多对一关系上。

如例子中的 CategoryNews.Categony 属于“一”,News 属于“多”。删除分类,该分类下的新闻也被删除了,这符合常理,但是如果新闻被删除了,分类也跟着删除,这就会造成数据混乱

4、merge

merge 是默认选项。在使用session.merge 合并对象时,会将使用了db.relationship 相关联的对象也进行 merge 操作。 这个参数几乎很少用到

5、expunge

进行移除操作时,会将相关联的对象也进行移除。

这个操作只是将对象从session中移除,并不会真正地从数据库中删除

1、News

python
class News(db.Model):
    ......
	category = db.relationship("Category",back_populates="newses",cascade= "expunge")

2、执行操作

python
news = News.query.first()
category =news.category
db.session.expunge(news)
category.name='测试分类"
db.session.commit()

使用 db.session.expunge 方法将news对象从 session 中移除,因为 news和 category 级联关系中设置了 expunge 选项,所以 category 对象也会跟着从 session 中移除,此时再去修改 category.name 的值,就不会同步到数据库中了

六、代码示例

1、新建Article表并与User表关联

python
class Article(db.Model):
    __tablename__="article"
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    title=db.Column(db.String(200),nullable=False)
    content=db.Column(db.Text,nullable=False)
    # 添加作者的外键
    author_id=db.Column(db.Integer,db.ForeignKey("user.id"))
    # backref:会自动的给User模型添加一个articles的属性,用来获取文章列表
    author=db.relationship("User",backref="articles")

2、插入数据

python
@app.route("/add")
def article_add():
    # article=Article(title="汤姆索尔历险记",content="索尔呜呜呜呜呜呜")
    article1=Article(title="西游记",content="孙悟空大战唐三藏")
    article2=Article(title="三国演义",content="吕布智取花果山")
    article1.author=User.query.get(5)
    article2.author=User.query.get(5)

    # 一次新增一条数据
    # db.session.add(article)
    # 一次新增一组数据
    db.session.add_all([article1,article2])
    db.session.commit()
    return "新增文章成功"

3、查询数据

python
@app.route("/query")
def article_query():
    # 查询用户所有的文章,参数传递肯定是一个userid返会文章所有数据,而User表通过backref已经有了artilces属性
    user=User.query.get(5)
    for article in user.articles:
        print(f"{article.title} {article.content}")