切换主题
四、表关系
一、外键
外键通过
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 参数实现的,这个参数的值可以为
all
、save-update
、merge
、refesh-expire
、expunge
、delete
中的一个或者多个,如果是多个,则通过英文逗号隔开,如“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
中移除,这样就把news
从category
上解除了关联因为在
Category
中定义newses
属性时,设置了cascade
为delete-orphan
,那么一旦解除关联,news对象就成为孤儿(orphan)对象,即会自动从数据库中被删除。这个选项一般用在一对多关系上,不能用在多对多以及多对一关系上。
如例子中的
Category
和News.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}")