網站首頁 編程語言 正文
這里以作者和文章來演示一對多的關系:一個作者可以有多篇文章,但是一篇文章只能有一個作者。
配置項
首先,配置下數據庫config.py
username = 'xxxx' password = 'xxxx' database = 'school' hostname = 'localhost' port = '3306' uri = f'mysql+pymysql://{username}:{password}@{hostname}:{port}/{database}' SQLALCHEMY_DATABASE_URI = uri SQLALCHEMY_TRACK_MODIFICATIONS = False
在app.py文件中導入配置
from flask import Flask from flask_sqlalchemy import SQLAlchemy import config app = Flask(__name__) app.config.from_object(config) db = SQLAlchemy(app)
接著創建模型類,建立python類到數據表的映射:
定義外鍵
class User(db.Model): __tablename__ = 'user' id = db.Column(db.Integer,primary_key=True,autoincrement=True) name = db.Column(db.String(100),nullable=False)
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) user_id = db.Column(db.Integer,db.ForeignKey('user.id'))
由于一個作者可以有多篇文章,所以外鍵應該設置在Article
類中,這樣每一篇文章的user_id
字段都只會有一個值,因為只對應一個作者。
假設,在User
類中存在外鍵字段article_id
,那么一個作者的所有文章都需要存放在這一個字段中,但是外鍵只能存放單一數據(表量),所以外鍵的設置總是在“多”
的這一側定義。
定義關系屬性
為什么需要關系屬性,具體的原因我也不清楚,我想可能是從查詢的角度來說,會更方便。
定義關系屬性需要使用關系函數。關系屬性在關系的出發側定義,即一對多關系的“一”
這一側。一個作者擁有多篇文章,我們在User模型類中,定義一個叫articles的關系屬性,用它可以表示每一個作者所對應的多篇文章。
class User(db.Model): __tablename__ = 'user' id = db.Column(db.Integer,primary_key=True,autoincrement=True) name = db.Column(db.String(100),nullable=False) articles = db.relationship('Article',backref=db.backref('user'))
articles 字段使用db.relationship()
關系函數定義為關系屬性,這個關系屬性將返回多個記錄。
- relationship()函數的第一個參數為關系另一側的模型名稱,是python類的名稱,不是數據表名稱。
- 第二個參數表示添加反向引用,會自動在另一側,也就是Article模型中,建立一個關系屬性,這個字段叫user,使用這個字段可以查找到文章所對應的用戶。
現在user表中多了一個字段articles,但是它并不是數據庫層面的,實際的表中并沒有這個字段,可以認為只是一個查詢接口。
接著創建數據表并插入數據:
u1 = User(name='zs') a1 = Article(title='西游記',content='西游記是四大名著') a1.author = u1 db.session.add(a1) db.session.commit()
創建了一個user表的記錄,一個article表的記錄,如何讓他們建立聯系呢?使用關系屬性字段:
文章唯一指向一個作者:
a1.author = u1
直接將作者的實例對象u1賦值給文章實例對象a1的author字段,他們就會建立關系,文章表中的user_id字段就會指向那個作者。
article表
user表
現在用戶zs擁有兩篇文章,嘗試使用關系屬性查詢。
u1 = User.query.filter_by(name='zs').first() print(u1.articles)
輸出user表的articles字段:
[<Article 1>, <Article 2>]
可以發現這個字段里面是兩條記錄,是article表中的兩條記錄,因為這兩篇文章都是zs的文章,所以通過這個關系屬性字段,可以獲取到一個作者對應的所有文章。
反過來,關于反向引用,backref=db.backref('user')
,它會在Article也建立一個關系屬性,這個字段叫做user,可以通過這個字段獲取到文章對應的作者。
比如:
#先找到文章 article1 = Article.query.filter_by(title='西游記').first() #使用反向引用的字段,user,獲取到這個文章對應的作者 print(article1.user)
輸出:
<User 1>
雙向的關系屬性
上面在建立關系屬性是,只是在User類中使用了關系屬性:
articles = db.relationship('Article',backref=db.backref('user'))
這種方式,會隱式的在Article類中也建立一個關系屬性user。我們可以使用back_populates
參數顯式的建立雙向的關系屬性。
這里仍然以作者author和文章article為例,一個作者可以有多篇文章,一篇文章只能有一個作者,建立顯示的雙向關系屬性。
class User(db.Model): __tablename__ = 'author' id = db.Column(db.Integer,primary_key=True,autoincrement=True) name = db.Column(db.String(100),nullable=False) #關系屬性 articles = db.relationship('Article',back_populates='authors') 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) auth_id = db.Column(db.Integer, db.ForeignKey('author.id')) #關系屬性 authors = db.relationship('User',back_populates='articles')
在一的一側,User類中,建立了關系屬性,articles,獲取一個作者對應的多個文章記錄。
在多的一側,Article類中,建立了關系屬性,authors,獲取每一篇文章對應的一個作者記錄。
使用back_populates
參數連接對方,參數值需要設置為關系另一側的關系屬性名。
使用關系屬性添加數據
u1 = User(name='zs') a1 = Article(title='西游記', content='西游記是四大名著') a2 = Article(title='紅樓夢', content='紅樓夢是四大名著') a1.authors = u1 a2.authors = u1 db.session.add_all([a1,a2]) db.session.commit()
實例化User類對象u1,Article類對象a1,a2。
然后使用Article類的關系屬性字段,authors
將User類對象u1賦值給Article類對象的關系屬性authors
或者反過來,使用用戶的關系屬性字段articles添加數據:
def insert(): u1 = User(name='zs') a1 = Article(title='西游記',content='西游記是四大名著') a2 = Article(title='水滸傳',content='水滸傳是四大名著') u1.articles = [a1,a2] db.session.add(u1) db.session.commit()
這里的添加方式是:u1.articles = [a1,a2]
接著查詢,使用關系屬性字段就可以查詢到了,這里只是演示了使用back_populates
參數顯示的建立雙向的關系屬性,之前使用的backref可以簡化關系的定義,是一種隱式的雙向關系的建立。
一對一
這里使用國家和首都演示一對一關系:每一個國家只有一個首都;反過來說,一個城市也只能作為一個國家的首都。
一對一關系實際上是通過建立雙向關系的一對多關系的基礎上轉化而來的。
class Country(db.Model): __tablename__ = 'country' id = db.Column(db.Integer,primary_key=True,autoincrement=True) name = db.Column(db.String(30),unique=True) capital = db.relationship('Capital',back_populates='country') class Capital(db.Model): __tablename__ = 'capital' id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String(30),unique=True) #外鍵在哪一個表中設置應該都可以 country_id = db.Column(db.Integer,db.ForeignKey('country.id')) #設置關系屬性 country = db.relationship('Country',back_populates='capital')
首先上面是一個雙向的關系屬性,這時候表的關系和之前的一對多沒有區別,現在我們需要給關系屬性中假如一個參數uselist=False
在”一“
的一側加入,就是在Country類模型中加入
capital = db.relationship('Capital',back_populates='country',uselist=False)
加入這個參數以后,在使用Country.capital獲取記錄時,將限制只返回一條記錄。
由于Capital中設置了外鍵country_id ,存儲單一數據,一個記錄的country_id
只會對應Country中的一個記錄。
這是一對多關系就變成了一對一關系。
測試一對一:
首先插入數據,插入一個國家記錄,一個城市記錄,然后建立關系。
def insert(): china = Country(name='中國') Beijing = Capital(name='北京') Beijing.country = china db.session.add(Beijing) db.session.commit()
這樣,Country表中就有了
capital表中:
他們目前時一對一的關系,那么,假如現在新增一個城市,它的country_id指向中國,
這時候就變成了一個國家,對應兩個城市,變成了一對多,但是我們定義的是一對一,這樣可行嗎?
def f(): #首先拿到中國的這條記錄 china = Country.query.filter_by(name='中國') china = china.first() print(china) #新增城市, Guangzhou = Capital(name='廣州') #建立廣州與中國之間的關系 Guangzhou.country = china db.session.add(Guangzhou) db.session.commit() print('success')
執行這個函數之后,數據表會有以下變化:
可以看到capital
表中的北京這條記錄的country_id
值變成了NULL,這正是因為我們建立的關系時一對一的,不允許變成一對多,所以,會把之前的對應關系取消掉。假如刪除了參數uselist=False
,就可以建立一對多的關系了。
多對多
這里使用學生和老師來演示多對多關系:每個學生有多個老師,每個老師也可以有多個學生。
在一對多關系中,我們可以在”多“這一側添加外鍵指向”一“這一側,外鍵只能存儲一個記錄,但是在多對多關系中,每一個記錄都可以與關系另一側的多個記錄建立關系,關系兩側的模型都需要存儲一組外鍵。
在SQLAlchemy中,要想表示多對多關系,除了關系兩側的模型外,我們還需要創建一個關聯表(association table)
。關聯表不存儲數據,只用來存儲關系兩側模型的外鍵對應關系,
from flask import Flask from flask_sqlalchemy import SQLAlchemy import config app = Flask(__name__) app.config.from_object(config) db = SQLAlchemy(app) #關聯表 association_table = db.Table('stu_tea_associ',#關聯表的名稱 db.Column('student_id',db.Integer,db.ForeignKey('student.id')),#字段student_id,類型,關聯的外鍵 db.Column('teacher_id',db.Integer,db.ForeignKey('teacher.id')))#字段teacher_id,類型,關聯的外鍵 #學生表 class Student(db.Model): __tablename__ = 'student' id = db.Column(db.Integer,primary_key=True,autoincrement=True) name = db.Column(db.String(20)) #建立關系屬性 teachers = db.relationship('Teacher',secondary=association_table,back_populates='students') #教師表 class Teacher(db.Model): __tablename__ = 'teacher' id = db.Column(db.Integer,primary_key=True,autoincrement=True) name = db.Column(db.String(20)) #建立關系屬性 students = db.relationship('Student', secondary=association_table, back_populates='teachers')
關聯表使用db.Table類定義,傳入的第一個參數是關聯表的名稱。我們在關聯表中定義了兩個字段student_id
,teacher_id
,這兩個字段作為外鍵,與student.id
,teacher.id
兩個字段關聯起來。
另外,這里建立的關系屬性是雙向的關系屬性,參數secondary='關聯表名稱'
。
接著,利用關系屬性添加數據
def insert(): stu1 = Student(name='小明') stu2 = Student(name='小紅') tea1 = Teacher(name='張三') tea2 = Teacher(name='李四') #學生1的老師有多個 #由于是多對多關系,所以會采用列表的形式進行外鍵的orm賦值 stu1.teachers = [tea1,tea2] #學生2的老師有多個 stu2.teachers = [tea1,tea2] db.session.add(stu1) db.session.add(stu2) db.session.commit()
調用關系屬性賦值的時候,這里需要使用列表的形式添加。
student:
teacher:
關聯表:association_table:
接著,使用關系屬性查詢數據;
def query(): #先查詢到小明這個同學 stu1 = Student.query.filter_by(name='小明').first() #使用關系屬性輸出對應的老師 print(stu1.teachers) #反過來,查找到老師,使用關系屬性輸出老師對應的所有學生 tea1 = Teacher.query.filter_by(name='張三').first() print(tea1.students)
輸出記錄
[<Teacher 1>, <Teacher 2>]
[<Student 1>, <Student 2>]
原文鏈接:https://blog.csdn.net/weixin_42576837/article/details/126501987
相關推薦
- 2022-12-22 go語言中GoMock安裝使用詳解_Golang
- 2022-01-31 為什么要使用3×3卷積?& 1*1卷積的作用是什么?& 對ResNet結構的一些理解
- 2023-03-05 Suspend函數與回調的互相轉換示例詳解_Android
- 2022-11-28 基于Python實現DIT-FFT算法_python
- 2022-06-17 教你Docker安裝GitLab功能_docker
- 2022-04-24 C語言字符函數中的isalnum()和iscntrl()你都知道嗎_C 語言
- 2022-11-16 Kotlin擴展方法超詳細介紹_Android
- 2023-02-01 Bat腳本-timeout?命令(延時執行)_DOS/BAT
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支