上篇文章學(xué)習(xí)了Flask框架——數(shù)據(jù)庫(kù)操作命令(查詢躯舔、修改驴剔、刪除、添加)庸毫,這篇文章我們學(xué)習(xí)Flask框架——模型關(guān)系(一對(duì)多關(guān)系)仔拟。
在一個(gè)完整的系統(tǒng)中,數(shù)據(jù)庫(kù)飒赃、數(shù)據(jù)表是必不可少的利花,數(shù)據(jù)表與表之間有著一定的聯(lián)系,互相影響著载佳,而Flask框架與數(shù)據(jù)庫(kù)有關(guān)的是模型炒事,那么如何使模型之間產(chǎn)生聯(lián)系呢。模型之間有兩種關(guān)系:一對(duì)多蔫慧、多對(duì)多挠乳。
在講解模型之間關(guān)系前,首先我們創(chuàng)建一個(gè)Flask項(xiàng)目姑躲,其目錄如下所示:
創(chuàng)建好項(xiàng)目后睡扬,我們開(kāi)始編寫配置文件settings.py的代碼,如下所示:
class Configs:
ENV='development'
DEBUG=True
# 設(shè)置連接數(shù)據(jù)庫(kù)路徑
SQLALCHEMY_DATABASE_URI='mysql+pymysql://root:123456@127.0.0.1:3306/test'
# 每次請(qǐng)求結(jié)束后自動(dòng)提交數(shù)據(jù)庫(kù)中的改動(dòng)
SQLALCHEMY_COMMIT_ON_TEARDOWN=True
# 禁用SQLAlchemy對(duì)追蹤對(duì)象的修改并且發(fā)送信號(hào)
SQLALCHEMY_TRACK_MODIFICATIONS = False
編寫好配置文件后黍析,我們將配置文件導(dǎo)入app.py文件中卖怜,代碼如所示:
from flask import Flask
from settings import Config
app = Flask(__name__)
app.config.from_object(Config) # 加載配置
if __name__ == '__main__':
app.run()
好了,F(xiàn)lask框架的基礎(chǔ)配置已經(jīng)寫好了阐枣,接下來(lái)我們將正式學(xué)習(xí)模型關(guān)系(一對(duì)多關(guān)系)马靠。
一對(duì)多關(guān)系
我們拿用戶與文章為例子,一個(gè)用戶可以發(fā)布多篇文章蔼两,所以它們是一對(duì)多關(guān)系甩鳄,我們假設(shè)數(shù)據(jù)庫(kù)中用戶表與文章表如下圖所示:
這里我們?cè)谖恼卤碇性黾右粋€(gè)user_id字段來(lái)標(biāo)志是哪個(gè)用戶發(fā)表的文章,當(dāng)我們想要找出張三所發(fā)表的所有文章時(shí)额划,只需要在文章表中搜索user_id為001的數(shù)據(jù)并輸出即可妙啃。
在數(shù)據(jù)庫(kù)中,數(shù)據(jù)表是這樣設(shè)計(jì)俊戳,那么如何使模型之間產(chǎn)生聯(lián)系呢彬祖,如何編寫模型之間產(chǎn)生聯(lián)系的代碼呢品抽?
ForeignKey外鍵
要想數(shù)據(jù)表產(chǎn)生聯(lián)系圆恤,主外鍵是必不可少的腔稀,在Flask框架中焊虏,可以通過(guò)ForeignKey方法在模型類中設(shè)置外鍵诵闭,這里我們?cè)谖恼翧rticle模型類中設(shè)置外鍵钉蒲,代碼如下所示:
from flask_sqlalchemy import SQLAlchemy
db=SQLAlchemy(app=app,use_native_unicode="utf8") # 創(chuàng)建映射對(duì)象db
class Article(db.Model):
__tablename__ = 'article' #數(shù)據(jù)表名
id=db.Column(db.Integer,primary_key=True,autoincrement=True) #文章id為主鍵俱笛,自增
title=db.Column(db.String(50),nullable=False) #文章標(biāo)題title
content=db.Column(db.Text,nullable=False) #文章內(nèi)容content
click_num=db.Column(db.Integer,default=0) #文章點(diǎn)擊數(shù)click_num敌呈,默認(rèn)值為0
user_id=db.Column(db.Integer,db.ForeignKey('user.id'),nullable=False) #設(shè)置user_id字段為user數(shù)據(jù)表的外鍵
我們使用db.ForeignKey()方法將user_id字段設(shè)置為user數(shù)據(jù)表的外鍵造寝,其方法傳入的參數(shù)為產(chǎn)生聯(lián)系的模型類數(shù)據(jù)表中的主鍵诫龙,注意:在一對(duì)多關(guān)系中赐稽,外鍵是設(shè)置在多的那方姊舵。
這樣article數(shù)據(jù)表與user數(shù)據(jù)表產(chǎn)生了聯(lián)系括丁,那么是不是這樣就可以了呢史飞?答案是:不是构资。
relationship()方法
有了外鍵后吐绵,還需要使用relationship()讓模型類之間產(chǎn)生聯(lián)系己单,這里我們?cè)赨ser用戶模型類中使用relationship()方法,代碼如下所示:
class User(db.Model):
__tablename__ = 'user' #數(shù)據(jù)表名
id=db.Column(db.Integer,primary_key=True,autoincrement=True) #文章id為主鍵纹份,自增
username=db.Column(db.String(15),nullable=False) #用戶名username
password=db.Column(db.String(64),nullable=False) #用戶密碼password
phone=db.Column(db.String(11),unique=True) #用戶手機(jī)號(hào)phone
isdelete=db.Column(db.Boolean,default=False) #字段isdelete蔓涧,用來(lái)判斷用戶是否被刪
articles=db.relationship('Article',backref='user',lazy='dynamic') #設(shè)置articles蠢笋,其作用為使User模型類與其他模型類產(chǎn)生聯(lián)系
我們使用了db.relationship()方法將該模型類與其他模型類產(chǎn)生聯(lián)系昨寞。其中:第一個(gè)參數(shù)為你要產(chǎn)生聯(lián)系的模型類援岩,這里我們要產(chǎn)生聯(lián)系的模型類為Article 享怀;第二個(gè)參數(shù)backref為反向引用添瓷,其參數(shù)值可以是任意的鳞贷,我們通過(guò)該參數(shù)值調(diào)用Article里面的屬性字段虐唠;第三個(gè)參數(shù)lazy為數(shù)據(jù)加載疆偿,其參數(shù)值可以為:
- select:默認(rèn)值杆故,SQLAlchemy 會(huì)使用一個(gè)標(biāo)準(zhǔn)的 select 語(yǔ)句必要時(shí)一次加載數(shù)據(jù)处铛;
- joined:告訴 SQLAlchemy 使用 JOIN 語(yǔ)句作為父級(jí)在同一查詢中來(lái)加載關(guān)系;
- subquery:類似joined篙贸,但是SQLAlchemy會(huì)使用子查詢爵川;
- dynamic:在有多條數(shù)據(jù)的時(shí)候是特別有用的寝贡,不是直接加載這些數(shù)據(jù)圃泡,SQLAlchemy會(huì)返回一個(gè)查詢對(duì)象颇蜡,在加載數(shù)據(jù)前我們可以過(guò)濾(提取)它們鳖目。
一般情況下领迈,lazy參數(shù)值我們會(huì)選擇默認(rèn)值狸捅,也就是說(shuō)我們不需要寫lazy參數(shù)尘喝。
注意:
- articles字段不會(huì)在數(shù)據(jù)表中體現(xiàn)出來(lái),articles字段主要作用于模板與視圖函數(shù)扯夭;
- db.relationship()只能定義在兩個(gè)要互相關(guān)聯(lián)的模型類中任意一個(gè)模型類交洗。
查看數(shù)據(jù)表
好了构拳,模型類的代碼已經(jīng)寫好了,接下來(lái)我們通過(guò)編寫代碼創(chuàng)建數(shù)據(jù)表斗埂,其代碼如下所示:
if __name__ == '__main__':
db.create_all() #創(chuàng)建數(shù)據(jù)表
app.run()
在終端執(zhí)行app.py文件后呛凶,就成功創(chuàng)建好數(shù)據(jù)表了。下面我們?cè)诮K端看看user模闲、article數(shù)據(jù)表尸折,如下圖所示:
可以發(fā)現(xiàn)在user數(shù)據(jù)表中,沒(méi)有articles字段收擦,該字段是使用了db.relationship()方法,而不是使用db.Column()方法宴猾,所以不會(huì)在數(shù)據(jù)表中展示該字段仇哆,該字段作用是使模型類與模型類之間產(chǎn)生聯(lián)系讹剔。
是不是模型關(guān)系——一對(duì)一關(guān)系就講完了呢延欠?NO沈跨,NO饿凛,NO,為了大家更直觀地感受模型關(guān)系——一對(duì)一關(guān)系心肪,我們通過(guò)編寫代碼使用模板來(lái)演示蒙畴。
實(shí)例演示
添加文章數(shù)據(jù)
首先我們?cè)赼pp.py文件中創(chuàng)建一個(gè)視圖函數(shù)膳凝,用來(lái)添加文章數(shù)據(jù)蹬音,代碼如下所示:
@app.route('/publish',methods=['GET','POST'])
def article():
if request.method=='POST': #當(dāng)請(qǐng)求為POST請(qǐng)求時(shí)
title=request.form.get('title') #獲取請(qǐng)求中的title
content=request.form.get('content') #獲取請(qǐng)求中的content
uid=request.form.get('uid') #獲取請(qǐng)求中的uid
article=Article() #創(chuàng)建文章模型類實(shí)例對(duì)象
#將獲取到的title著淆、content永部、uid數(shù)據(jù)傳遞到實(shí)例對(duì)象article的title苔埋、content、user_id中
article.title=title
article.content=content
article.user_id=uid
#將獲取到的數(shù)據(jù)保存在article數(shù)據(jù)表中
db.session.add(article) #添加數(shù)據(jù)
db.session.commit() #提交事務(wù)
return '添加成功'
else:
#當(dāng)不是POST請(qǐng)求時(shí)
users=User.query.filter(User.isdelete==False).all() #找出所有user數(shù)據(jù)表中isdelete為False的數(shù)據(jù)
return render_template('add_article.html',users=users) #通過(guò)render_template()方法渲染add_article.html并傳遞users到網(wǎng)頁(yè)中
編寫好視圖函數(shù)后荞膘,接下來(lái)在templates文件夾中創(chuàng)建一個(gè)名為add_article的html文件羽资,其代碼如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加文章</title>
</head>
<body>
<form action="{{ url_for('article') }}" method="post">
<p><input type="text" name="title" placeholder="文章標(biāo)題"></p>
<p>
<textarea cols="50" rows="10" placeholder="輸入文章內(nèi)容" name="content"></textarea>
</p>
<p>
用戶:
<select name="uid">
<option value="0">請(qǐng)選擇用戶</option>
{% for user in users %}
<option value="{{ user.id }}">{{ user.username }}</option>
{% endfor %}
</select>
</p>
<p><input type="submit" value="添加文章"></p>
</form>
</body>
</html>
在運(yùn)行我們Flask項(xiàng)目前,我們先在user添加幾條數(shù)據(jù)腹暖,如下圖所示:
添加數(shù)據(jù)后微服,我們?cè)诮K端執(zhí)行app.py缨历,并在瀏覽器中打開(kāi)http://127.0.0.1:5000/publish辛孵,如下圖所示:
當(dāng)我們點(diǎn)擊添加文章時(shí)魄缚,就會(huì)顯示添加成功冶匹,添加成功后嚼隘,我們?cè)偬砑訋讞l數(shù)據(jù)飞蛹,如下圖所示:
好了卧檐,成功添加文章后,那么我們?nèi)绾卧诰W(wǎng)頁(yè)中展示文章標(biāo)題捕仔、文章內(nèi)容以及作者名呢逻澳?在article數(shù)據(jù)表中是沒(méi)有作者名的斜做,只有user_id瓤逼,那么如何獲取到作者名呢库物?
通過(guò)文章獲取作者名
通過(guò)文章獲取作者名戚揭,首先我們?cè)赼pp.py文件中編寫視圖函數(shù)民晒,代碼如下所示:
@app.route('/all_article')
def all_article():
articles=Article.query.all() #查詢所有文章數(shù)據(jù)
return render_template('all_article.html',articles=articles) #通過(guò)render_template()方法渲染all_article.html并傳遞articles到網(wǎng)頁(yè)中
編寫好視圖函數(shù)后,接下來(lái)在templates文件夾中創(chuàng)建一個(gè)名為all_article的html文件靴姿,其代碼如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{# 獲取文章數(shù)據(jù)列表 #}
{% for article in articles %}
<div id="container">
<p>
<h2>{{ article.title }}</h2>
<div>作者:{{ article.user.username }}</div>
</p>
<p>{{ article.content }}</p>
</div>
{% endfor %}
</body>
</html>
首先我們通過(guò)視圖函數(shù)傳遞的articles獲取到文章列表宵晚,再通過(guò)articles遍歷獲取文章內(nèi)容與標(biāo)題淤刃,注意第十二行代碼吱型,剛才我們?cè)谙肴绾潍@取作者名唁影,在編寫用戶模型類時(shí)添加了articles字段据沈,其代碼如下所示:
articles=db.relationship('Article',backref='user',lazy='dynamic')
這個(gè)反向引用backref參數(shù)值user就很用有了锌介,我們通過(guò)article.user獲取user模型類對(duì)象孔祸,這樣就有了user的屬性了,再通過(guò).username獲取user中的username屬性拂蝎,這個(gè)屬性也就是作者名了温自,在終端執(zhí)行app.py皇钞,并在瀏覽器中打開(kāi)http://127.0.0.1:5000/all_article夹界,如下圖所示:
這樣我們成功獲取到了文章標(biāo)題、內(nèi)容以舒、作者名。
通過(guò)作者找出其文章
問(wèn)題來(lái)了永票,當(dāng)我們通過(guò)作者來(lái)找出其所有文章數(shù)據(jù),該如何編寫代碼呢键俱?
首先我們創(chuàng)建一個(gè)視圖函數(shù)编振,代碼如下所示:
@app.route('/author')
def author():
id=request.args.get('id') #獲取請(qǐng)求的id號(hào)
user=User.query.get(id) #找出請(qǐng)求id的用戶數(shù)據(jù)
return render_template('author.html',user=user) #通過(guò)render_template()方法渲染author.html并傳遞user到網(wǎng)頁(yè)中
編寫好視圖函數(shù)后踪央,接下來(lái)在templates文件夾中創(chuàng)建一個(gè)名為author的html文件畅蹂,其代碼如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{# 獲取文章數(shù)據(jù)列表 #}
{% for article in user.articles %}
<div id="container">
<p>
<h2>{{ article.title }}</h2>
<div>作者:{{ user.username }}</div>
</p>
<p>{{ article.content }}</p>
</div>
{% endfor %}
</body>
</html>
該模板author.html文件與通過(guò)文章獲取作者名的all_article.html文件類似液斜,只是第八行和第十二行代碼發(fā)生了改變叠穆。
第八行代碼硼被,將視圖函數(shù)傳遞的user中.articles獲取文章數(shù)據(jù)對(duì)象嚷硫,獲取到文章數(shù)據(jù)對(duì)象后就有了該對(duì)象的屬性论巍,這個(gè)articles就是我們User模型類中的articles字段,user.articles返回的是文章數(shù)據(jù)列表丹禀,第十二行代碼直接通過(guò)user.username獲取到用戶名双泪。
在終端執(zhí)行app.py,并在瀏覽器中打開(kāi)http://127.0.0.1:5000/author?id=1葫盼,如下圖所示:
這里我們獲取了用戶id為1贫导,也就是用戶名為zhangsan的文章數(shù)據(jù)孩灯。
好了峰档,F(xiàn)lask框架——模型關(guān)系(一對(duì)多關(guān)系)的相關(guān)知識(shí)就講到這里了讥巡,下篇文章繼續(xù)Flask框架——模型關(guān)系(多對(duì)多關(guān)系)尚卫,感謝觀看J臁M饫铩!
公眾號(hào):白巧克力LIN