Flask框架——模型關(guān)系(1對(duì)多)

上篇文章學(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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鳖链,一起剝皮案震驚了整個(gè)濱河市芙委,隨后出現(xiàn)的幾起案子狂秦,更是在濱河造成了極大的恐慌裂问,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皮壁,死亡現(xiàn)場(chǎng)離奇詭異蛾魄,居然都是意外死亡滴须,警方通過(guò)查閱死者的電腦和手機(jī)茉稠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)膀篮,“玉大人岂膳,你說(shuō)我怎么就攤上這事谈截◆の梗” “怎么了喻鳄?”我有些...
    開(kāi)封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵再菊,是天一觀的道長(zhǎng)颜曾。 經(jīng)常有香客問(wèn)我泛豪,道長(zhǎng),這世上最難降的妖魔是什么吕粹? 我笑而不...
    開(kāi)封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮聚请,結(jié)果婚禮上驶赏,老公的妹妹穿的比我還像新娘既鞠。我一直安慰自己煤傍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布嘱蛋。 她就那樣靜靜地躺著蚯姆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪洒敏。 梳的紋絲不亂的頭發(fā)上龄恋,一...
    開(kāi)封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音凶伙,去河邊找鬼郭毕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛函荣,可吹牛的內(nèi)容都是我干的煮落。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼睦疫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼葫松!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起珊擂,我...
    開(kāi)封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤扛稽,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后瞧掺,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體夏跷,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡猫态,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年义辕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蘸吓。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡摸航,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情排监,我是刑警寧澤嫁佳,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布腾夯,位于F島的核電站娃惯,受9級(jí)特大地震影響愕提,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鼓黔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸寇钉。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春占贫,著一層夾襖步出監(jiān)牢的瞬間桩引,已是汗流浹背血崭。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工月匣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓剑按,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親缩擂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子博脑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容