Flask sqlalchemy一對(duì)多與多對(duì)一與一對(duì)一及多對(duì)多關(guān)系介紹
這里以作者和文章來演示一對(duì)多的關(guān)系:一個(gè)作者可以有多篇文章,但是一篇文章只能有一個(gè)作者。
配置項(xiàng)
首先,配置下數(shù)據(jù)庫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文件中導(dǎo)入配置
from flask import Flask from flask_sqlalchemy import SQLAlchemy import config app = Flask(__name__) app.config.from_object(config) db = SQLAlchemy(app)
接著創(chuàng)建模型類,建立python類到數(shù)據(jù)表的映射:
定義外鍵
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'))
由于一個(gè)作者可以有多篇文章,所以外鍵應(yīng)該設(shè)置在Article類中,這樣每一篇文章的user_id字段都只會(huì)有一個(gè)值,因?yàn)橹粚?duì)應(yīng)一個(gè)作者。
假設(shè),在User類中存在外鍵字段article_id,那么一個(gè)作者的所有文章都需要存放在這一個(gè)字段中,但是外鍵只能存放單一數(shù)據(jù)(表量),所以外鍵的設(shè)置總是在“多”的這一側(cè)定義。
定義關(guān)系屬性
為什么需要關(guān)系屬性,具體的原因我也不清楚,我想可能是從查詢的角度來說,會(huì)更方便。
定義關(guān)系屬性需要使用關(guān)系函數(shù)。關(guān)系屬性在關(guān)系的出發(fā)側(cè)定義,即一對(duì)多關(guān)系的“一”這一側(cè)。一個(gè)作者擁有多篇文章,我們?cè)赨ser模型類中,定義一個(gè)叫articles的關(guān)系屬性,用它可以表示每一個(gè)作者所對(duì)應(yīng)的多篇文章。
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()關(guān)系函數(shù)定義為關(guān)系屬性,這個(gè)關(guān)系屬性將返回多個(gè)記錄。
- relationship()函數(shù)的第一個(gè)參數(shù)為關(guān)系另一側(cè)的模型名稱,是python類的名稱,不是數(shù)據(jù)表名稱。
- 第二個(gè)參數(shù)表示添加反向引用,會(huì)自動(dòng)在另一側(cè),也就是Article模型中,建立一個(gè)關(guān)系屬性,這個(gè)字段叫user,使用這個(gè)字段可以查找到文章所對(duì)應(yīng)的用戶。
現(xiàn)在user表中多了一個(gè)字段articles,但是它并不是數(shù)據(jù)庫層面的,實(shí)際的表中并沒有這個(gè)字段,可以認(rèn)為只是一個(gè)查詢接口。
接著創(chuàng)建數(shù)據(jù)表并插入數(shù)據(jù):
u1 = User(name='zs') a1 = Article(title='西游記',content='西游記是四大名著') a1.author = u1 db.session.add(a1) db.session.commit()
創(chuàng)建了一個(gè)user表的記錄,一個(gè)article表的記錄,如何讓他們建立聯(lián)系呢?使用關(guān)系屬性字段:
文章唯一指向一個(gè)作者:
a1.author = u1
直接將作者的實(shí)例對(duì)象u1賦值給文章實(shí)例對(duì)象a1的author字段,他們就會(huì)建立關(guān)系,文章表中的user_id字段就會(huì)指向那個(gè)作者。
article表

user表

現(xiàn)在用戶zs擁有兩篇文章,嘗試使用關(guān)系屬性查詢。
u1 = User.query.filter_by(name='zs').first() print(u1.articles)
輸出user表的articles字段:
[<Article 1>, <Article 2>]
可以發(fā)現(xiàn)這個(gè)字段里面是兩條記錄,是article表中的兩條記錄,因?yàn)檫@兩篇文章都是zs的文章,所以通過這個(gè)關(guān)系屬性字段,可以獲取到一個(gè)作者對(duì)應(yīng)的所有文章。
反過來,關(guān)于反向引用,backref=db.backref('user'),它會(huì)在Article也建立一個(gè)關(guān)系屬性,這個(gè)字段叫做user,可以通過這個(gè)字段獲取到文章對(duì)應(yīng)的作者。
比如:
#先找到文章 article1 = Article.query.filter_by(title='西游記').first() #使用反向引用的字段,user,獲取到這個(gè)文章對(duì)應(yīng)的作者 print(article1.user)
輸出:
<User 1>
雙向的關(guān)系屬性
上面在建立關(guān)系屬性是,只是在User類中使用了關(guān)系屬性:
articles = db.relationship('Article',backref=db.backref('user'))
這種方式,會(huì)隱式的在Article類中也建立一個(gè)關(guān)系屬性u(píng)ser。我們可以使用back_populates參數(shù)顯式的建立雙向的關(guān)系屬性。
這里仍然以作者author和文章article為例,一個(gè)作者可以有多篇文章,一篇文章只能有一個(gè)作者,建立顯示的雙向關(guān)系屬性。
class User(db.Model):
__tablename__ = 'author'
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
name = db.Column(db.String(100),nullable=False)
#關(guān)系屬性
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'))
#關(guān)系屬性
authors = db.relationship('User',back_populates='articles')
在一的一側(cè),User類中,建立了關(guān)系屬性,articles,獲取一個(gè)作者對(duì)應(yīng)的多個(gè)文章記錄。
在多的一側(cè),Article類中,建立了關(guān)系屬性,authors,獲取每一篇文章對(duì)應(yīng)的一個(gè)作者記錄。
使用back_populates參數(shù)連接對(duì)方,參數(shù)值需要設(shè)置為關(guān)系另一側(cè)的關(guān)系屬性名。
使用關(guān)系屬性添加數(shù)據(jù)
u1 = User(name='zs') a1 = Article(title='西游記', content='西游記是四大名著') a2 = Article(title='紅樓夢(mèng)', content='紅樓夢(mèng)是四大名著') a1.authors = u1 a2.authors = u1 db.session.add_all([a1,a2]) db.session.commit()
實(shí)例化User類對(duì)象u1,Article類對(duì)象a1,a2。
然后使用Article類的關(guān)系屬性字段,authors
將User類對(duì)象u1賦值給Article類對(duì)象的關(guān)系屬性authors
或者反過來,使用用戶的關(guān)系屬性字段articles添加數(shù)據(jù):
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]
接著查詢,使用關(guān)系屬性字段就可以查詢到了,這里只是演示了使用back_populates參數(shù)顯示的建立雙向的關(guān)系屬性,之前使用的backref可以簡化關(guān)系的定義,是一種隱式的雙向關(guān)系的建立。
一對(duì)一
這里使用國家和首都演示一對(duì)一關(guān)系:每一個(gè)國家只有一個(gè)首都;反過來說,一個(gè)城市也只能作為一個(gè)國家的首都。
一對(duì)一關(guān)系實(shí)際上是通過建立雙向關(guān)系的一對(duì)多關(guān)系的基礎(chǔ)上轉(zhuǎn)化而來的。
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)
#外鍵在哪一個(gè)表中設(shè)置應(yīng)該都可以
country_id = db.Column(db.Integer,db.ForeignKey('country.id'))
#設(shè)置關(guān)系屬性
country = db.relationship('Country',back_populates='capital')
首先上面是一個(gè)雙向的關(guān)系屬性,這時(shí)候表的關(guān)系和之前的一對(duì)多沒有區(qū)別,現(xiàn)在我們需要給關(guān)系屬性中假如一個(gè)參數(shù)uselist=False
在”一“的一側(cè)加入,就是在Country類模型中加入
capital = db.relationship('Capital',back_populates='country',uselist=False)加入這個(gè)參數(shù)以后,在使用Country.capital獲取記錄時(shí),將限制只返回一條記錄。
由于Capital中設(shè)置了外鍵country_id ,存儲(chǔ)單一數(shù)據(jù),一個(gè)記錄的country_id 只會(huì)對(duì)應(yīng)Country中的一個(gè)記錄。
這是一對(duì)多關(guān)系就變成了一對(duì)一關(guān)系。
測(cè)試一對(duì)一:
首先插入數(shù)據(jù),插入一個(gè)國家記錄,一個(gè)城市記錄,然后建立關(guān)系。
def insert():
china = Country(name='中國')
Beijing = Capital(name='北京')
Beijing.country = china
db.session.add(Beijing)
db.session.commit()這樣,Country表中就有了

capital表中:

他們目前時(shí)一對(duì)一的關(guān)系,那么,假如現(xiàn)在新增一個(gè)城市,它的country_id指向中國,
這時(shí)候就變成了一個(gè)國家,對(duì)應(yīng)兩個(gè)城市,變成了一對(duì)多,但是我們定義的是一對(duì)一,這樣可行嗎?
def f():
#首先拿到中國的這條記錄
china = Country.query.filter_by(name='中國')
china = china.first()
print(china)
#新增城市,
Guangzhou = Capital(name='廣州')
#建立廣州與中國之間的關(guān)系
Guangzhou.country = china
db.session.add(Guangzhou)
db.session.commit()
print('success')執(zhí)行這個(gè)函數(shù)之后,數(shù)據(jù)表會(huì)有以下變化:


可以看到capital表中的北京這條記錄的country_id值變成了NULL,這正是因?yàn)槲覀兘⒌年P(guān)系時(shí)一對(duì)一的,不允許變成一對(duì)多,所以,會(huì)把之前的對(duì)應(yīng)關(guān)系取消掉。假如刪除了參數(shù)uselist=False,就可以建立一對(duì)多的關(guān)系了。
多對(duì)多
這里使用學(xué)生和老師來演示多對(duì)多關(guān)系:每個(gè)學(xué)生有多個(gè)老師,每個(gè)老師也可以有多個(gè)學(xué)生。
在一對(duì)多關(guān)系中,我們可以在”多“這一側(cè)添加外鍵指向”一“這一側(cè),外鍵只能存儲(chǔ)一個(gè)記錄,但是在多對(duì)多關(guān)系中,每一個(gè)記錄都可以與關(guān)系另一側(cè)的多個(gè)記錄建立關(guān)系,關(guān)系兩側(cè)的模型都需要存儲(chǔ)一組外鍵。
在SQLAlchemy中,要想表示多對(duì)多關(guān)系,除了關(guān)系兩側(cè)的模型外,我們還需要?jiǎng)?chuàng)建一個(gè)關(guān)聯(lián)表(association table)。關(guān)聯(lián)表不存儲(chǔ)數(shù)據(jù),只用來存儲(chǔ)關(guān)系兩側(cè)模型的外鍵對(duì)應(yīng)關(guān)系,
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import config
app = Flask(__name__)
app.config.from_object(config)
db = SQLAlchemy(app)
#關(guān)聯(lián)表
association_table = db.Table('stu_tea_associ',#關(guān)聯(lián)表的名稱
db.Column('student_id',db.Integer,db.ForeignKey('student.id')),#字段student_id,類型,關(guān)聯(lián)的外鍵
db.Column('teacher_id',db.Integer,db.ForeignKey('teacher.id')))#字段teacher_id,類型,關(guān)聯(lián)的外鍵
#學(xué)生表
class Student(db.Model):
__tablename__ = 'student'
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
name = db.Column(db.String(20))
#建立關(guān)系屬性
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))
#建立關(guān)系屬性
students = db.relationship('Student', secondary=association_table, back_populates='teachers')關(guān)聯(lián)表使用db.Table類定義,傳入的第一個(gè)參數(shù)是關(guān)聯(lián)表的名稱。我們?cè)陉P(guān)聯(lián)表中定義了兩個(gè)字段student_id,teacher_id,這兩個(gè)字段作為外鍵,與student.id,teacher.id兩個(gè)字段關(guān)聯(lián)起來。
另外,這里建立的關(guān)系屬性是雙向的關(guān)系屬性,參數(shù)secondary='關(guān)聯(lián)表名稱'。
接著,利用關(guān)系屬性添加數(shù)據(jù)
def insert():
stu1 = Student(name='小明')
stu2 = Student(name='小紅')
tea1 = Teacher(name='張三')
tea2 = Teacher(name='李四')
#學(xué)生1的老師有多個(gè)
#由于是多對(duì)多關(guān)系,所以會(huì)采用列表的形式進(jìn)行外鍵的orm賦值
stu1.teachers = [tea1,tea2]
#學(xué)生2的老師有多個(gè)
stu2.teachers = [tea1,tea2]
db.session.add(stu1)
db.session.add(stu2)
db.session.commit()調(diào)用關(guān)系屬性賦值的時(shí)候,這里需要使用列表的形式添加。
student:

teacher:

關(guān)聯(lián)表:association_table:

接著,使用關(guān)系屬性查詢數(shù)據(jù);
def query():
#先查詢到小明這個(gè)同學(xué)
stu1 = Student.query.filter_by(name='小明').first()
#使用關(guān)系屬性輸出對(duì)應(yīng)的老師
print(stu1.teachers)
#反過來,查找到老師,使用關(guān)系屬性輸出老師對(duì)應(yīng)的所有學(xué)生
tea1 = Teacher.query.filter_by(name='張三').first()
print(tea1.students)輸出記錄
[<Teacher 1>, <Teacher 2>]
[<Student 1>, <Student 2>]
到此這篇關(guān)于Flask sqlalchemy一對(duì)多與多對(duì)一與一對(duì)一及多對(duì)多關(guān)系介紹的文章就介紹到這了,更多相關(guān)Flask sqlalchemy內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python super( )函數(shù)用法總結(jié)
今天給大家?guī)淼闹R(shí)是關(guān)于Python的相關(guān)知識(shí),文章圍繞著super( )函數(shù)展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06
OpenCV圖像形態(tài)學(xué)的實(shí)現(xiàn)
本文主要介紹了OpenCV圖像形態(tài)學(xué)的實(shí)現(xiàn),包括腐蝕、膨脹、開運(yùn)算、閉運(yùn)算、梯度運(yùn)算、頂帽運(yùn)算和黑帽運(yùn)算,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-04-04
python實(shí)現(xiàn)對(duì)數(shù)組按指定列排序
這篇文章主要介紹了python實(shí)現(xiàn)對(duì)數(shù)組按指定列排序方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02
python正則表達(dá)式匹配不包含某幾個(gè)字符的字符串方法
今天小編就為大家分享一篇python正則表達(dá)式匹配不包含某幾個(gè)字符的字符串方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-07-07

