2019-02-10

Flask基礎(chǔ)介紹

Flask 是一個(gè)輕量級(jí)的 Web 應(yīng)用框架, 使用 Python 編寫?;?WerkzeugWSGI 工具箱和 Jinja2 模板引擎。使用 BSD 授權(quán)。Flask 也被稱為 microframework ,因?yàn)樗褂煤?jiǎn)單的核心,用 extension 增加其它功能。Flask 沒有默認(rèn)使用的數(shù)據(jù)庫、窗體驗(yàn)證工具。然而,F(xiàn)lask 保留了擴(kuò)增的彈性,可以用 Flask-extension 加入這些功能:ORM、窗體驗(yàn)證工具、文件上傳、各種開放式身份驗(yàn)證技術(shù)。

Flask搭建博客步驟

博客功能

根據(jù)配置文件中的認(rèn)證允許用戶登錄以及注銷。僅僅支持一個(gè)用戶。

當(dāng)用戶登錄后,他們可以添加新的條目,這些條目是由純文本的標(biāo)題和 HTML 的正文構(gòu)成。因?yàn)槲覀冃湃斡脩暨@里的 HTML 是安全的。

頁面倒序顯示所有條目(新的條目在前),并且用戶登入后可以在此添加新條目。

步驟

1.創(chuàng)建文件夾

在開始之前,讓我們?yōu)檫@個(gè)應(yīng)用創(chuàng)建需要的文件夾:

/flaskr

?? /static

?? /templates

其中:

flaskr:項(xiàng)目總文件夾。其中會(huì)存放數(shù)據(jù)庫代碼文件 schema.sql 和 主代碼文件 flaskr.py。

static:項(xiàng)目的資源文件。用于存放 css 和 javascript 文件。

templates:項(xiàng)目前端頁面文件。用于存放前端頁面模板。

2.數(shù)據(jù)庫模式

首先我們要?jiǎng)?chuàng)建數(shù)據(jù)庫模式。對(duì)于這個(gè)應(yīng)用僅一張表就足夠了,而且我們只想支持 SQLite ,所以很簡(jiǎn)單。在 Code/flaskr 目錄下新建 schema.sql 文件并向其中寫入如下代碼:

droptableif exists entries;

createtableentries (

idintegerprimary key autoincrement,

title stringnotnull,

textstringnotnull

);

這個(gè)模式由一個(gè)稱為 entries 的單表構(gòu)成,在這個(gè)表中每行包含一個(gè) id ,一個(gè) title 和一個(gè) text。id 是一個(gè)自增的整數(shù)而且是主鍵,其余兩個(gè)為非空的字符串。

3.應(yīng)用設(shè)置代碼

現(xiàn)在我們已經(jīng)有了數(shù)據(jù)庫模式了,可以創(chuàng)建應(yīng)用的模塊了。在 Code/flaskr 目錄下新建 flaskr.py 文件。對(duì)于小應(yīng)用,直接把配置放在主模塊里,正如我們現(xiàn)在要做的一樣,這是可行的。然而一個(gè)更干凈的解決方案就是單獨(dú)創(chuàng)建.ini或者.py文件接著加載或者導(dǎo)入里面的值。在 flaskr.py 文件中寫入如下代碼:

# 導(dǎo)入所有的模塊

importsqlite3

fromflaskimportFlask,request,session,g,redirect,url_for,abort,render_template,flash

# 配置文件

DATABASE='/tmp/flaskr.db'

ENV='development'

DEBUG=True

SECRET_KEY='development key'

USERNAME='admin'

PASSWORD='default'

下一步我們能夠創(chuàng)建真正的應(yīng)用,修改 flaskr.py 文件在其中配置初始化:

# 創(chuàng)建應(yīng)用

app=Flask(__name__)

app.config.from_object(__name__)

from_object() 將會(huì)尋找給定的對(duì)象(如果它是一個(gè)字符串,則會(huì)導(dǎo)入它),搜尋里面定義的全部大寫的變量。在我們的這種情況中,配置文件就是我們上面寫的幾行代碼。 你也可以將他們分別存儲(chǔ)到多個(gè)文件。通常從配置文件中加載配置是一個(gè)好的主意。這時(shí)可以使用 from_envvar() 來實(shí)現(xiàn),可以用它替換上面的 from_object():

app.config.from_envvar('FLASKR_SETTINGS',silent=True)

這種方法我們可以設(shè)置一個(gè)名為 FLASKR_SETTINGS 的環(huán)境變量來設(shè)定一個(gè)配置文件載入后是否覆蓋默認(rèn)值。靜默開關(guān) silent=True 告訴 Flask 不去關(guān)心這個(gè)環(huán)境變量鍵值是否存在。SECRET_KEY 是為了保持客戶端的會(huì)話安全。明智地選擇該鍵,使得它難以猜測(cè),最好是盡可能復(fù)雜。

DEBUG 調(diào)試標(biāo)志啟用或禁用交互式調(diào)試。決不讓調(diào)試模式在生產(chǎn)系統(tǒng)中啟動(dòng),因?yàn)樗鼘⒃试S用戶在服務(wù)器上執(zhí)行代碼!

我們還添加了一個(gè)輕松地連接到指定數(shù)據(jù)庫的方法,這個(gè)方法用于在請(qǐng)求時(shí)打開一個(gè)連接,并且在交互式 Python shell 和腳本中也能使用,這將方便后面的使用。

在 Code/flaskr.py 文件中繼續(xù)添加如下代碼:

defconnect_db():

returnsqlite3.connect(app.config['DATABASE'])

最后如果我們想要把這個(gè)文件當(dāng)做獨(dú)立應(yīng)用來運(yùn)行,只需在服務(wù)器啟動(dòng)文件也就是 Code/flaskr.py 文件的末尾添加這一行:

if__name__=='__main__':

app.run()

4.創(chuàng)建數(shù)據(jù)庫

如前面所述,F(xiàn)laskr 是一個(gè)數(shù)據(jù)庫驅(qū)動(dòng)的應(yīng)用程序,準(zhǔn)確地來說,F(xiàn)laskr 是一個(gè)使用關(guān)系數(shù)據(jù)庫系統(tǒng)的應(yīng)用程序。這樣的系統(tǒng)需要一個(gè)模式告訴它們?nèi)绾未鎯?chǔ)信息。因此在首次啟動(dòng)服務(wù)器之前,創(chuàng)建數(shù)據(jù)庫模式是很重要的。

在 Code/flaskr.py 文件中添加如下代碼:

fromcontextlibimportclosing

接著我們可以創(chuàng)建一個(gè)稱為 init_db 函數(shù),該函數(shù)用來初始化數(shù)據(jù)庫。為此我們可以使用之前定義的 connect_db 函數(shù)。 只要在 Code/flaskr.py 文件中的 connect_db 函數(shù)下添加這樣的函數(shù):

def init_db():

? ? with closing(connect_db()) as db:

? ? ? ? with app.open_resource('schema.sql') as f:

? ? ? ? ? ? db.cursor().executescript(f.read().decode())

? ? ? ? db.commit()

closing()函數(shù)允許我們?cè)?with 塊中保持?jǐn)?shù)據(jù)庫連接可用。應(yīng)用對(duì)象的 open_resource() 方法在其方框外也支持這個(gè)功能,因此可以在 with 塊中直接使用。這個(gè)函數(shù)從當(dāng)前位置(你的flaskr 文件夾)中打開一個(gè)文件,并且允許你讀取它。我們?cè)谶@里用它在數(shù)據(jù)庫連接上執(zhí)行一個(gè)腳本。

當(dāng)我們連接到數(shù)據(jù)庫時(shí)會(huì)得到一個(gè)數(shù)據(jù)庫連接對(duì)象(這里命名它為 db),這個(gè)對(duì)象提供給我們一個(gè)數(shù)據(jù)庫指針。指針上有一個(gè)可以執(zhí)行完整腳本的方法。最后我們不顯式地提交更改, SQLite 3 或者其它事務(wù)數(shù)據(jù)庫不會(huì)這么做。

現(xiàn)在可以在 Python shell 里創(chuàng)建數(shù)據(jù)庫,導(dǎo)入并調(diào)用剛才的函數(shù):

python3

>>> from flaskr import init_db

>>> init_db()

5.請(qǐng)求數(shù)據(jù)庫連接

所有我們的函數(shù)中都需要數(shù)據(jù)庫連接,因此在請(qǐng)求之前初始化它們,在請(qǐng)求結(jié)束后自動(dòng)關(guān)閉它們就很有意義。

Flask 允許我們使用 before_request() ,after_request() 和 teardown_request() 裝飾器來實(shí)現(xiàn)這個(gè)功能,在 Code/flaskr.py 文件中添加如下代碼:

@app.before_request

def before_request():

? ? g.db = connect_db()

@app.teardown_request

def teardown_request(exception):

? ? g.db.close()

使用 before_request() 裝飾器的函數(shù)會(huì)在請(qǐng)求之前被調(diào)用而且不帶參數(shù)。使用 after_request() 裝飾器的函數(shù)會(huì)在請(qǐng)求之后被調(diào)用且傳入將要發(fā)給客戶端的響應(yīng)。

它們必須返回那個(gè)響應(yīng)對(duì)象或是不同的響應(yīng)對(duì)象。但當(dāng)異常拋出時(shí),它們不一定會(huì)被執(zhí)行,這時(shí)可以使用 teardown_request() 裝飾器。它裝飾的函數(shù)將在響應(yīng)構(gòu)造后執(zhí)行,并不允許修改請(qǐng)求,返回的值會(huì)被忽略。如果在請(qǐng)求已經(jīng)被處理的時(shí)候拋出異常,它會(huì)被傳遞到每個(gè)函數(shù),否則會(huì)傳入一個(gè) None。

我們把當(dāng)前的數(shù)據(jù)庫連接保存在 Flask 提供的 g 特殊對(duì)象中。這個(gè)對(duì)象只能保存一次請(qǐng)求的信息,并且在每個(gè)函數(shù)里都可用。不要用其它對(duì)象來保存信息,因?yàn)樵诙嗑€程環(huán)境下將不可行。特殊的對(duì)象 g 在后臺(tái)有一些神奇的機(jī)制來保證它在做正確的事情。

6.建立視圖

現(xiàn)在數(shù)據(jù)庫連接已經(jīng)正常工作,可以開始編寫視圖函數(shù)。我們需要四個(gè)視圖函數(shù):

顯示條目

這個(gè)視圖顯示所有存儲(chǔ)在數(shù)據(jù)庫中的條目。它監(jiān)聽著應(yīng)用的根地址以及將會(huì)從數(shù)據(jù)庫中查詢標(biāo)題和內(nèi)容。 id 值最大的條目(最新的條目)將在最前面。從游標(biāo)返回的行是按 select 語句中聲明的列組織的元組。對(duì)于像我們這樣的小應(yīng)用是足夠的,但是你可能要把它們轉(zhuǎn)換成字典,如果你對(duì)如何轉(zhuǎn)換成字典感興趣的話,請(qǐng)查閱簡(jiǎn)化查詢例子。

視圖函數(shù)將會(huì)把條目作為字典傳入 show_entries.html 模板并返回渲染結(jié)果。

在 flaskr/flaskr.py 文件中添加如下代碼:

@app.route('/')

def show_entries():

? ? cur = g.db.execute('select title, text from entries order by id desc')? # 查詢語句

? ? entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]? # 將查詢結(jié)果轉(zhuǎn)換為字典

? ? return render_template('show_entries.html', entries=entries)

添加新條目

這個(gè)視圖允許登錄的用戶添加新的條目。它只回應(yīng) POST 請(qǐng)求,實(shí)際的表單是顯示在 show_entries 頁面。如果正常工作的話,我們用 flash() 向下一個(gè)請(qǐng)求閃現(xiàn)一條信息并且跳轉(zhuǎn)回 show_entries 頁面。

在 flaskr/flaskr.py 文件中添加如下代碼:

@app.route('/add', methods=['POST'])

def add_entry():

? ? if not session.get('logged_in'):

? ? ? ? abort(401)

? ? g.db.execute('insert into entries (title, text) values (?, ?)',[request.form['title'], request.form['text']])? # 向數(shù)據(jù)庫中插入數(shù)據(jù)

? ? g.db.commit()? # 更新數(shù)據(jù)

? ? flash('New entry was successfully posted')? # 閃現(xiàn)一條消息

? ? return redirect(url_for('show_entries'))

注意我們這里檢查用戶登錄情況( logged_in 鍵存在會(huì)話中,并且為 True )。向 SQL 語句中傳遞參數(shù),需要在語句中使用問號(hào) ? 來代替參數(shù),并把參數(shù)放在列表中進(jìn)行傳遞。不要使用字符串格式化的方式直接把參數(shù)傳入 SQL 語句中,這樣可能會(huì)有潛在的 SQL 注入風(fēng)險(xiǎn)。

登錄

這些函數(shù)是用于用戶登錄以及注銷。登錄時(shí)依據(jù)在配置中的值檢查用戶名和密碼并且在會(huì)話中設(shè)置 logged_in 鍵值。如果用戶成功登錄,logged_in 鍵值被設(shè)置成 True ,并跳轉(zhuǎn)回 show_entries 頁面。此外,會(huì)有消息閃現(xiàn)來提示用戶登錄成功。

如果發(fā)生錯(cuò)誤,模板會(huì)通知,并提示重新登錄。在 flaskr/flaskr.py 文件中添加如下代碼:

@app.route('/login', methods=['GET', 'POST'])

def login():

? ? error = None

? ? if request.method == 'POST':

? ? ? ? if request.form['username'] != app.config['USERNAME']:? # 如果用戶名不符合

? ? ? ? ? ? error = 'Invalid username'

? ? ? ? elif request.form['password'] != app.config['PASSWORD']:? # 如果密碼不符合

? ? ? ? ? ? error = 'Invalid password'

? ? ? ? else:

? ? ? ? ? ? session['logged_in'] = True? # 成功登錄,在 session 中添加一個(gè) logged_in 值為 True

? ? ? ? ? ? flash('You were logged in')? # 閃現(xiàn)一條消息

? ? ? ? ? ? return redirect(url_for('show_entries'))? # 重定向到首頁

? ? return render_template('login.html', error=error)? # 如果沒有成功登錄則返回登錄頁面以及錯(cuò)誤信息

注銷

另一方面,注銷函數(shù)從會(huì)話中移除了 logged_in 鍵值。這里我們使用一個(gè)大絕招:如果你使用字典的 pop() 方法并傳入第二個(gè)參數(shù)(默認(rèn)),這個(gè)方法會(huì)從字典中刪除這個(gè)鍵,如果這個(gè)鍵不存在則什么都不做。這很有用,因?yàn)槲覀儾恍枰獧z查用戶是否已經(jīng)登入。

在 flaskr/flaskr.py 文件中添加如下代碼:

@app.route('/logout')

def logout():

? ? session.pop('logged_in', None)? # 移除 logged_in 鍵

? ? flash('You were logged out')? # 閃現(xiàn)消息

? ? return redirect(url_for('show_entries'))? # 重定向到首頁

7.模板

現(xiàn)在我們應(yīng)該開始編寫模板。

layout.html

這個(gè)模板包含 HTML 主體結(jié)構(gòu),標(biāo)題和一個(gè)登錄鏈接(或者當(dāng)用戶已登錄則提供注銷)。如果有閃現(xiàn)信息的話它也將顯示閃現(xiàn)信息。{% block body %} 塊能夠被子模板中的同樣名字( body )的塊替代。

session 字典在模板中同樣可用的,你能用它檢查用戶是否登錄。注意在 Jinja 中你可以訪問不存在的對(duì)象/字典屬性或成員,如同下面的代碼,即便 logged_in 鍵不存在,仍然可以正常工作。

在 flaskr/templates 目錄下新建 layout.html 文件并寫入如下代碼:

<!doctype html>

<title>Flaskr</title>

<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">

<div class=page>

? <h1>Flaskr</h1>

? <div class=metanav>

? {% if not session.logged_in %}

? ? <a href="{{ url_for('login') }}">log in</a>

? {% else %}

? ? <a href="{{ url_for('logout') }}">log out</a>

? {% endif %}

? </div>

? {% for message in get_flashed_messages() %}

? ? <div class=flash>{{ message }}</div>

? {% endfor %}

? {% block body %}{% endblock %}

</div>

show_entries.html

這個(gè)模板繼承了上面的layout.html模板用來顯示信息。注意for遍歷了所有我們用render_template()函數(shù)傳入的信息。我們同樣告訴表單提交到add_entry函數(shù)通過使用 HTTP 的POST方法。

在 flaskr/templates 目錄下新建 show_entries.html 文件并寫入如下代碼:

{% extends "layout.html" %}

{% block body %}

? {% if session.logged_in %}

? ? <form action="{{ url_for('add_entry') }}" method=post class=add-entry>

? ? ? <dl>

? ? ? ? <dt>Title:

? ? ? ? <dd><input type=text size=30 name=title>

? ? ? ? <dt>Text:

? ? ? ? <dd><textarea name=text rows=5 cols=40></textarea>

? ? ? ? <dd><input type=submit value=Share>

? ? ? </dl>

? ? </form>

? {% endif %}

? <ul class=entries>

? {% for entry in entries %}

? ? <li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}

? {% else %}

? ? <li><em>Unbelievable.? No entries here so far</em>

? {% endfor %}

? </ul>

{% endblock %}

login.html

最后是登錄模板,基本上只顯示一個(gè)允許用戶登錄的表單。

在 flaskr/templates 目錄下新建 login.html 文件并寫入如下代碼:

{% extends "layout.html" %}

{% block body %}

? <h2>Login</h2>

? {% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}

? <form action="{{ url_for('login') }}" method=post>

? ? <dl>

? ? ? <dt>Username:

? ? ? <dd><input type=text name=username>

? ? ? <dt>Password:

? ? ? <dd><input type=password name=password>

? ? ? <dd><input type=submit value=Login>

? ? </dl>

? </form>

{% endblock %}

添加樣式

現(xiàn)在其它一切都正常工作,是時(shí)候給應(yīng)用添加些樣式。

在 Code/flaskr/static 目錄下新建 style.css 樣式文件并寫入如下代碼:

body? ? ? ? ? ? { font-family: sans-serif; background: #eee; }

a, h1, h2? ? ? { color: #377BA8; }

h1, h2? ? ? ? ? { font-family: 'Georgia', serif; margin: 0; }

h1? ? ? ? ? ? ? { border-bottom: 2px solid #eee; }

h2? ? ? ? ? ? ? { font-size: 1.2em; }

.page? ? ? ? ? { margin: 2em auto; width: 35em; border: 5px solid #ccc;

? ? ? ? ? ? ? ? ? padding: 0.8em; background: white; }

.entries? ? ? ? { list-style: none; margin: 0; padding: 0; }

.entries li? ? { margin: 0.8em 1.2em; }

.entries li h2? { margin-left: -1em; }

.add-entry? ? ? { font-size: 0.9em; border-bottom: 1px solid #ccc; }

.add-entry dl? { font-weight: bold; }

.metanav? ? ? ? { text-align: right; font-size: 0.8em; padding: 0.3em;

? ? ? ? ? ? ? ? ? margin-bottom: 1em; background: #fafafa; }

.flash? ? ? ? ? { background: #CEE5F5; padding: 0.5em;

? ? ? ? ? ? ? ? ? border: 1px solid #AACBE2; }

.error? ? ? ? ? { background: #F0D6D6; padding: 0.5em; }

8.運(yùn)行應(yīng)用

這樣我們就完成了簡(jiǎn)單博客應(yīng)用的代碼編寫

$ python3 flaskr.py

* Serving Flask app "flaskr" (lazy loading)

* Environment: development

* Debug mode: on

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

* Restarting with stat

* Debugger is active!

* Debugger PIN: 183-564-157

訪問首頁 http://127.0.0.1:5000 ,即可開啟博客

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 科學(xué)怎么不美啊,科學(xué)有時(shí)候比神話還美呢。 想想生物學(xué)解釋的生命起源,人類由原子出發(fā),在水里具化出柔軟的軀體。為了見...
    林夢(mèng)致閱讀 162評(píng)論 0 0
  • 文/小肥羊 她永遠(yuǎn)也不會(huì)忘記,這短短幾個(gè)月間發(fā)生的事情。 從被通知辭退所有女員工,到搬離個(gè)人物品離開公司,只有十分...
    進(jìn)化論evo閱讀 403評(píng)論 0 1
  • 這是一份感悟,基本也屬于裹腳布性質(zhì)的絮叨,昨天吃過年夜飯,陸陸續(xù)續(xù)外地的同事回家,留守的繼續(xù)干活,我也能靜下回看這...
    朱雷震莊生曉夢(mèng)閱讀 562評(píng)論 2 2
  • 2018.10.15. 秋高氣爽大熊山, 仙人谷里聽喊泉。 飛云天橋玻璃道, 美女尖叫聲連連!
    楊玉偉閱讀 1,261評(píng)論 1 6

友情鏈接更多精彩內(nèi)容