pythonFlask框架學習

Flask是由python實現(xiàn)的一個web微框架,讓我們可以使用Python語言快速實現(xiàn)一個網(wǎng)站或Web服務。而且有對應的python3及python2版本。首先這邊選擇的是python3.6,雖然python3在網(wǎng)上好像名聲不咋地,而且一度有文章說python3正在毀滅Python,但是反正是別人選的,也就將就了。

在網(wǎng)上看別人下載個flask很麻煩,反正我的很簡單,windows環(huán)境下的

1.安裝Flask

pip install flask

2.目錄結(jié)構(gòu)

通過別人的目錄大致了解一下flask框架的目錄結(jié)構(gòu)。

flask-demo/
  ├ run.py           # 應用啟動程序
  ├ config.py        # 環(huán)境配置
  ├ requirements.txt # 列出應用程序依賴的所有Python包
  ├ tests/           # 測試代碼包
  │   ├ __init__.py 
  │   └ test_*.py    # 測試用例
  └ myapp/
      ├ admin/       # 藍圖目錄
      ├ static/
      │   ├ css/     # css文件目錄
      │   ├ img/     # 圖片文件目錄
      │   └ js/      # js文件目錄
      ├ templates/   # 模板文件目錄
      ├ __init__.py    
      ├ forms.py     # 存放所有表單,如果多,將其變?yōu)橐粋€包
      ├ models.py    # 存放所有數(shù)據(jù)模型,如果多,將其變?yōu)橐粋€包
      └ views.py     # 存放所有視圖函數(shù),如果多,將其變?yōu)橐粋€包

3.開始 Hello world

最簡單的測試

from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
    return 'Hello World'
if __name__ == '__main__':
    app.debug = True # 設置調(diào)試模式,生產(chǎn)模式的時候要關掉debug
    app.run()

這是flask框架制作的一個最小的應用。使用python運行后訪問localhost:5000就能看到網(wǎng)頁顯示Hello world。
這里首先引入了Flask類,然后給這個類創(chuàng)建了一個實例,name代表這個模塊的名字。因為這個模塊是直接被運行的所以此時name的值是main。然后用route()這個修飾器定義了一個路由,告訴flask如何訪問該函數(shù)。最后用run()函數(shù)使這個應用在服務器上運行起來。

4.模板

Flask的模板功能是基于Jinja2模板引擎實現(xiàn)的。讓我們來實現(xiàn)一個例子吧。創(chuàng)建一個新的Flask運行文件(你應該不會忘了怎么寫吧),代碼如下:

from  flask import  Flask

from  flask import  render_template

app  =  Flask(__name__)

@app.route('/hello')

@app.route('/hello/<name>')

def  hello(name=None):

    return  render_template('hello.html',  name=name)

if  __name__  ==  '__main__':

    app.run(host='0.0.0.0',  debug=True)

這段代碼”hello()”函數(shù)并不是直接返回字符串,而是調(diào)用了”render_template()”方法來渲染模板。方法的第一個參數(shù)”hello.html”指向你想渲染的模板名稱,第二個參數(shù)”name”是你要傳到模板去的變量,變量可以傳多個。

那么這個模板”hello.html”在哪兒呢,變量參數(shù)又該怎么用呢?接下來我們創(chuàng)建模板文件。在當前目錄下,創(chuàng)建一個子目錄”templates”(注意,一定要使用這個名字)。然后在”templates”目錄下創(chuàng)建文件”hello.html”,內(nèi)容如下:

<!doctype html>

<title>Hello Sample</title>

{% if name %}

  <h1>Hello {{ name }}!</h1>

{% else %}

  <h1>Hello World!</h1>

{%  endif  %}

這段代碼是不是很像HTML?接觸過其他模板引擎的朋友們肯定立馬秒懂了這段代碼。它就是一個HTML模板,根據(jù)”name”變量的值,顯示不同的內(nèi)容。變量或表達式由”{{ }}”修飾,而控制語句由”{% %}”修飾,其他的代碼,就是我們常見的HTML。

讓我們打開瀏覽器,輸入”http://localhost:5000/hello/man”,頁面上即顯示大標題”Hello man!”。我們再看下頁面源代碼

<!doctype html>

<title>Hello from Flask</title>

  <h1>Hello man!</h1>

果然,模板代碼進入了”Hello {{ name }}!”分支,而且變量”{{ name }}”被替換為了”man”。Jinja2的模板引擎還有更多強大的功能,包括for循環(huán),過濾器等。模板里也可以直接訪問內(nèi)置對象如request, session等。對于Jinja2的細節(jié),感興趣的朋友們可以自己去查查。

5.模板繼承

一般我們的網(wǎng)站雖然頁面多,但是很多部分是重用的,比如頁首,頁腳,導航欄之類的。對于每個頁面,都要寫這些代碼,很麻煩。Flask的Jinja2模板支持模板繼承功能,省去了這些重復代碼。讓我們基于上面的例子,在”templates”目錄下,創(chuàng)建一個名為”layout.html”的模板:

<!doctype html>

<title>Hello Sample</title>

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

<div class="page">

    {% block body %}

    {% endblock %}

</div>

再修改之前的”hello.html”,把原來的代碼定義在”block body”中,并在代碼一開始”繼承”上面的”layout.html”:

{%  extends  "layout.html"  %}

{%  block  body  %}

{%  if  name  %}

  <h1>Hello {{ name }}!</h1>

{% else %}

  <h1>Hello World!</h1>

{%  endif  %}

{%  endblock  %}

打開瀏覽器,再看下”http://localhost:5000/hello/man”頁面的源碼。

<!doctype html>

<title>Hello Sample</title>

<link rel="stylesheet"  type="text/css"  href="/static/style.css">

<div class="page">

  <h1>Hello man!</h1>

</div>

你會發(fā)現(xiàn),雖然”render_template()”加載了”hello.html”模板,但是”layout.html”的內(nèi)容也一起被加載了。而且”hello.html”中的內(nèi)容被放置在”layout.html”中”{% block body %}”的位置上。形象的說,就是”hello.html”繼承了”layout.html”。

6. HTML自動轉(zhuǎn)義

我們看下下面的代碼:

@app.route('/')

def  index():

    return  '<div>Hello %s</div>'  %  '<em>Flask</em>'

打開頁面,你會看到”Hello Flask”字樣,而且”Flask”是斜體的,因為我們加了”em”標簽。但有時我們并不想讓這些HTML標簽自動轉(zhuǎn)義,特別是傳遞表單參數(shù)時,很容易導致HTML注入的漏洞。我們把上面的代碼改下,引入”Markup”類:

from  flask import  Flask,  Markup

app  =  Flask(__name__)

@app.route('/')

def  index():

    return  Markup('<div>Hello %s</div>')  %  '<em>Flask</em>'

再次打開頁面,”em”標簽顯示在頁面上了。Markup還有很多方法,比如”escape()”呈現(xiàn)HTML標簽, “striptags()”去除HTML標簽。這里就不一一列舉了。

7.Request 對象

from flask import Flask,url_for,request,render_template

@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'POST':
        if request.form['user'] == 'admin':
            return 'Admin login successfully!'
        else:
            return 'No such user!'
    title = request.args.get('title', 'Default')
    return render_template('login.html', title=title)
if __name__ == "__main__":
    app.run(debug=True)

簡單解釋下,request中”method”變量可以獲取當前請求的方法,即”GET”, “POST”, “DELETE”, “PUT”等;”form”變量是一個字典,可以獲取Post請求表單中的內(nèi)容,在上例中,如果提交的表單中不存在”user”項,則會返回一個”KeyError”,你可以不捕獲,頁面會返回400錯誤(想避免拋出這”KeyError”,你可以用request.form.get(“user”)來替代)。而”request.args.get()”方法則可以獲取Get請求URL中的參數(shù),該函數(shù)的第二個參數(shù)是默認值,當URL參數(shù)不存在時,則返回默認值。

在當前目錄下,創(chuàng)建一個子目錄”templates”(注意,一定要使用這個名字)。然后在”templates”目錄下,添加
layout.html

<!doctype html>
<title>Hello Sample</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<div class="page">
    {% block body %}
    {% endblock %}
</div>

login.html

{% extends "layout.html" %}
{% block body %}
<form name="login" action="/login" method="post">
    Hello {{ title }}, please login by:
    <input type="text" name="user" />
</form>
{% endblock %}
圖片.png
圖片.png

8.會話session

會話可以用來保存當前請求的一些狀態(tài),以便于在請求之前共享信息。

from flask import Flask,url_for,request,render_template,redirect,session
@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'POST':
        if request.form['user'] == 'admin':
            session['user'] = request.form['user']
            return 'Admin login successfully!'
        else:
            return 'No such user!'
    if 'user' in session:
        return 'Hello %s!' % session['user']
    else:
        title = request.args.get('title', 'Default')
        return render_template('login.html', title=title)
@app.route('/logout')
def logout():
    session.pop('user', None)
    return redirect(url_for('login'))

app.secret_key = '123456'
if __name__ == "__main__":
    app.run(debug=True)

你可以看到,”admin”登陸成功后,再打開”login”頁面就不會出現(xiàn)表單了。然后打開logout頁面可以登出。session對象的操作就跟一個字典一樣。特別提醒,使用session時一定要設置一個密鑰”app.secret_key”,如上例。不然你會得到一個運行時錯誤,內(nèi)容大致是”RuntimeError: the session is unavailable because no secret key was set”。密鑰要盡量復雜,最好使用一個隨機數(shù),這樣不會有重復,上面的例子不是一個好密鑰。

9.構(gòu)建響應

在之前的例子中,請求的響應我們都是直接返回字符串內(nèi)容,或者通過模板來構(gòu)建響應內(nèi)容然后返回。其實我們也可以先構(gòu)建響應對象,設置一些參數(shù)(比如響應頭)后,再將其返回。修改下上例中的Get請求部分:

from flask import Flask,url_for,request,render_template,redirect,session,make_response

@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'POST':
        ...
    if 'user' in session:
        ...
    else:
        title = request.args.get('title', 'Default')
        response = make_response(render_template('login.html', title=title), 200)
        response.headers['key'] = 'value'
        return response
if __name__ == "__main__":
    app.run(debug=True)

打開瀏覽器調(diào)試,在Get請求用戶未登錄狀態(tài)下,你會看到響應頭中有一個”key”項?!眒ake_response”方法就是用來構(gòu)建response對象的,第二個參數(shù)代表響應狀態(tài)碼,缺省就是200。

圖片.png

10.Cookie的使用

提到了Session,當然也要介紹Cookie嘍,畢竟沒有Cookie,Session就根本沒法用(不知道為什么?查查去)。Flask中使用Cookie也很簡單:

from flask import Flask,url_for,request,render_template,redirect,session,make_response
import time
@app.route('/login', methods=['POST', 'GET'])
def login():
    response = None
    if request.method == 'POST':
        if request.form['user'] == 'admin':
            session['user'] = request.form['user']
            response = make_response('Admin login successfully!')
            response.set_cookie('login_time', time.strftime('%Y-%m-%d %H:%M:%S'))
        ...

    else:
        if 'user' in session:
            login_time = request.cookies.get('login_time')
            response = make_response('Hello %s, you logged in on %s' % (session['user'], login_time))
        ...

    return response
app.secret_key = '123456'
if __name__ == "__main__":
    app.run(debug=True)

這次我們引入了”time”模塊來獲取當前系統(tǒng)時間。我們在返回響應時,通過”response.set_cookie()”函數(shù),來設置Cookie項,之后這個項值會被保存在瀏覽器中。這個函數(shù)的第三個參數(shù)(max_age)可以設置該Cookie項的有效期,單位是秒,不設的話,在瀏覽器關閉后,該Cookie項即失效。

在請求中,”request.cookies”對象就是一個保存了瀏覽器Cookie的字典,使用其”get()”函數(shù)就可以獲取相應的鍵值。

圖片.png

11.錯誤處理

使用”abort()”函數(shù)可以直接退出請求,返回錯誤代碼:

rom flask import Flask,abort

app = Flask(__name__)
@app.route('/error')
def error():
    abort(404)

if __name__ == "__main__":
    app.run(debug=True)

上例會顯示瀏覽器的404錯誤頁面。有時候,我們想要在遇到特定錯誤代碼時做些事情,或者重寫錯誤頁面,可以用下面的方法:

rom flask import Flask,abort

app = Flask(__name__)

@app.errorhandler(404)
def page_not_found(error):
    return render_template('404.html'), 404
if __name__ == "__main__":
    app.run(debug=True)
圖片.png

不過,在實際開發(fā)過程中,我們并不會經(jīng)常使用”abort()”來退出,常用的錯誤處理方法一般都是異常的拋出或捕獲。裝飾器”@app.errorhandler()”除了可以注冊錯誤代碼外,還可以注冊指定的異常類型。讓我們來自定義一個異常:

class InvalidUsage(Exception):
    status_code = 400

    def __init__(self, message, status_code=400):
        Exception.__init__(self)
        self.message = message
        self.status_code = status_code


@app.errorhandler(InvalidUsage)
def invalid_usage(error):
    response = make_response(error.message)
    response.status_code = error.status_code
    return response
@app.route('/exception')
def exception():
    raise InvalidUsage('No privilege to access the resource', status_code=403)
if __name__ == "__main__":
    app.run(debug=True)
圖片.png

我們在上面的代碼中定義了一個異?!盜nvalidUsage”,同時我們通過裝飾器”@app.errorhandler()”修飾了函數(shù)”invalid_usage()”,裝飾器中注冊了我們剛定義的異常類。這也就意味著,一但遇到”InvalidUsage”異常被拋出,這個”invalid_usage()”函數(shù)就會被調(diào)用。

12.url重定向

Flask的URL規(guī)則是基于Werkzeug的路由模塊。模塊背后的思想是基于 Apache 以及更早的 HTTP 服務器主張的先例,保證優(yōu)雅且唯一的 URL。

@app.route('/projects/')
def projects():
    return 'The project page'

@app.route('/about')
def about():
    return 'The about page'

訪問第一個路由不帶/時,F(xiàn)lask會自動重定向到正確地址。
訪問第二個路由時末尾帶上/后Flask會直接報404 NOT FOUND錯誤。

重定向”redirect()”函數(shù)的使用在上面例子中已有出現(xiàn)。作用就是當客戶端瀏覽某個網(wǎng)址時,將其導向到另一個網(wǎng)址。常見的例子,比如用戶在未登錄時瀏覽某個需授權(quán)的頁面,我們將其重定向到登錄頁要求其登錄先。

from flask import session, redirect
 
@app.route('/')
def index():
    if 'user' in session:
        return 'Hello %s!' % session['user']
    else:
        return redirect(url_for('login'), 302)

“redirect()”的第二個參數(shù)時HTTP狀態(tài)碼,可取的值有301, 302, 303, 305和307,默認即302(為什么沒有304?留給大家去思考)。
轉(zhuǎn)載來源:思誠之道

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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