好久沒有更新過了,把這些日子弄的東西先放1波出來
這個學習筆記未必會包括全部章節,有時會將兩個章節進行合并
虛擬環境是 Python 解釋器的1個私有副本,可以安裝私有包而不影響全局 Python 解釋器
Python 3.3 通過 venv 模塊原生支持虛擬環境,命令為 pyvenv 。不過在 Python 3.3 中使用 pyvenv 命令創建的虛擬環境不包括 pip ,在 Python 3.4 中改進了這1缺點。
我這邊使用 Python 3.5.0 和 Pycharm 來進行學習,所以暫時不使用這個,Pycharm 可以很好的解決各個項目的版本的虛擬環境問題。
使用 (venv) $ pip install flask
可以在虛擬環境中安裝 Flask
Web 服務器使用1種名為 Web 服務器網關接口的協議,把接收自客戶真個所有要求都轉交給這個對象處理
Flask 類的構造函數只有1個必須指定的參數,即程序主模塊或包的名字。Flask 用這個參數決定程序的根目錄,以便找到相對程序根目錄的資源文件位置。在大多數程序中,Python 的 __name__
變量就是所需的值。
程序實例保存了1個 URL 到 Python 函數的映照關系,處理 URL 和函數之間關系的程序成為路由。
Flask 中定義路由最簡單的方式是使用程序提供的 app.route 修飾器,把修飾的函數注冊為路由。
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
修飾器是 Python 語言的標準特性,可使用不同的方式修改函數的行動
把 index 函數注冊為程序跟地址的處理程序,不過在 Pthon 代碼中嵌入響應字符串會致使代碼難以保護,以后將會介紹生成響應的正確方法。像 index 這樣的函數稱為視圖函數。
地址中也能夠包括可變部份,可以在路由中定義可變的這部份
@app.route('/user/<name>')
def user(name):
return '<h1>Hello, %s!</h1>' % name
尖括號中的內容是動態部份,任何能匹配靜態部份的 URL 都會映照到這個路由上。
用 run 方法來啟動 Flask 集成的開發 Web 服務器
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
if __name__ == '__main__':
app.run(debug=True)
使用__name__ == '__main__'
是 Python 的慣用法,確保直接履行此腳本時才啟動服務器,若其他程序調用該腳本可能父級程序會啟動不同的服務器,就不履行 app.run 了
在 Pycharm 中啟動后,可以看到 Flask 為我們啟動了1個調試實例
通過閱讀器訪問,可以發現已成功的訪問了,而且調試器中也記錄下了這次訪問
我們可以和前面的動態部份結合起來,變成1個根據不同地址顯示不同頁面的服務,可以看到服務器已記錄下了兩次訪問,其中1次使用路徑進來,響應中也帶有相應信息,表示已成功
為了不在需要使用訪問對象時,都將其作為參數傳入。Flask 使用上下文臨時把某些對象變成全局可訪問
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route('/')
def index():
user_agent = request.headers.get('User-Agent')
return '<p>Your Browser is %s!</p>' % user_agent
if __name__ == '__main__':
app.run(debug=True)
在這個視圖函數中,我們把 request 當作全局變量使用,事實上 request 不多是全局變量,而 Flask 使用上下文讓特定的變量在1個線程中全局可訪問,與此同時還不會干擾其他線程。
線程是可單獨管理的最小指令集,多線程 Web 服務器會創建1個線程池
再從線程池當選擇1個線程用于處理接收到的要求
在 Flask 中有兩種上下文:程序上下文和要求上下文,這兩種上下文提供的變量以下:
變量名 | 上下文 | 說明 |
---|---|---|
current_app | 程序上下文 | 當前激活程序的程序實例 |
g | 程序上下文 | 處理要求時用作臨時存儲的對象,每次要求都會重設這個變量 |
request | 要求上下文 | 要求對象,封裝了客戶端發出的 HTTP 要求中的內容 |
session | 要求上下文 | 用戶繪畫,用戶存儲要求之間需要“記住”的值的字典 |
Flask 會在分發要求之前激活程序和要求上下文,要求處理完成后再將其刪除
Flask 使用 app.route 修飾器或非修飾器情勢的 app.add_url_rule() 生成映照
想要查看 Flask 程序中的 URL 映照是甚么模樣,我們可以在 Python Shell 中檢查為 hello》py 生成的映照
from hello import app
app.url_map
有時在處理要求之前或以后履行代碼會很有用,Flask 提供了注冊通用函數的功能,注冊的函數可在要求被分發到視圖函數之前或以后調用。要求鉤子使用修飾器實現。Flask 支持以下4種鉤子:
鉤子名 | 上下文 |
---|---|
before_first_request | 注冊1個函數,在處理第1個要求之前運行 |
before_request | 注冊1個函數,在每次要求之前運行 |
after_request | 注冊1個函數,如果沒有未處理的異常拋出,在每次要求后運行 |
teardown_request | 注冊1個函數,即便有未處理的異常拋出,也在每次要求后運行 |
Flask 會在分發要求之前激活程序和要求上下文,要求處理完成后再將其刪除
在要求鉤子函數和視圖函數之間同享數據1般使用上下文全局變量g
Flask 調用視圖函數后,會將其返回值作為響應的內容。如果視圖函數返回的響應需要使用不同的狀態碼,那末可以把數字代碼作為第2個返回值,添加到響應文本以后。
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route('/')
def index():
return '<p>Bad Request !</p>', 400
if __name__ == '__main__':
app.run(debug=True)
可以看到閱讀器中直接顯示了 HTPP 400 毛病。視圖函數返回的響應還可以接受第3個參數,這是1個由首部組成的字典,可以添加到 HTTP 響應中。
如果不想返回由多個值組成的元組,Flask 視圖函數還可以返回 Response 對象。make_response() 函數可接受1/2/3個參數,并返回1個 Response 對象。
from flask import Flask
from flask import make_response
from flask import redirect
app = Flask(__name__)
@app.route('/')
def index():
response = make_response('<h1>This document carries a cookie!</h1>')
response.set_cookie('answer', '42')
return response
@app.route('/<name>')
def user(name):
return redirect('http://www.baidu.com')
if __name__ == '__main__':
app.run(debug=True)
可以看到,已被設置好了 Cookie
如果在地址中訪問/txb
則會被重定向到百度去
還有1種特殊的響應由 abort 函數生成,用于毛病處理
from flask import Flask
from flask import make_response
from flask import abort
app = Flask(__name__)
@app.route('/')
def index():
response = make_response('<h1>This document carries a cookie!</h1>')
response.set_cookie('answer', '42')
return response
@app.route('/get/<id>')
def get_id(id):
if id != '9':
abort(404)
return '<h1>Hello, %s!</h1>' % id
if __name__ == '__main__':
app.run(debug=True)
通過訪問可以發現,只有在訪問/get/9
時才能正確得到 Hello ,若是輸入其他返回為 404 頁面
abort 不會把控制權交還給調用它的函數,而是拋出異常把控制權交給 Web 服務器
Flask 的開發 Web 服務器支持很多啟動設置選項,但只能在腳本中作為參數傳給 app.run() 函數,這類方式其實不10分方便,傳遞設置選項的理想方式是使用命令行參數。
Flask-Script 是1個 Flask 擴大,為 Flask 程序添加了1個命令行解析器。
from flask import Flask
from flask import make_response
from flask import redirect
from flask import abort
from flask.ext.script import Manager
app = Flask(__name__)
manager = Manager(app)
@app.route('/')
def index():
response = make_response('<h1>This document carries a cookie!</h1>')
response.set_cookie('answer', '42')
return response
@app.route('/user/<name>')
def user(name):
return redirect('http://www.baidu.com')
@app.route('/get/<id>')
def get_id(id):
if id != '9':
abort(404)
return '<h1>Hello, %s!</h1>' % id
if __name__ == '__main__':
manager.run()
這個擴大的初始化方法為:把程序實例作為參數傳給構造函數,初始化主類的實例。創建的對象可以在各個擴大中使用。在這里,服務器由 manager.run() 啟動,啟動后就可以解析命令行了。
在命令行中啟動試試看:
Shell 命令用于在程序的上下文中啟動 Python Shell 會話,可以用來運行保護任務或測試,還可以調試異常
runserver 命令用來啟動 Web 服務器
視圖函數的作用很明確,即生成要求的響應
模版是1個包括響應文本的文件,其中包括用占位符變量表示的動態部份
默許情況下,Flask 在程敘文件夾中的 templates 子文件夾中尋覓模版
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/user/<name>')
def user(name):
return render_template('user.html', name=name)
if __name__ == '__main__':
app.run()
這段代碼可以和上面實現相同的作用,render_template()
函數的第1個參數是模版的文件名,隨后的參數都是鍵值對,表示模版中變量對應的真實值
模版中使用的{{ name }}
結構表示1個變量,它是1種特殊的占位符,告知模版引擎這個位置的值從渲染模版時使用的數據中獲得
Jinja2 能辨認所有類型的變量,乃至是1些復雜的類型,例如列表、字典和對象,例如
<p>A value from a dictionary: {{ mydict['key'] }}.</p>
<p>A value froma list: {{ mylist[3] }}.</p>
<p>A value froma list, with a variable index: {{ mylist[myintvar] }}.</p>
<p>A value froman object's method: {{ myobj.somemethod() }}.</p>
下面這個例子就是讀取這些復雜類型的
from flask import Flask
from flask import render_template
app = Flask(__name__)
class Human():
def somemethod(self):
return 'Flask say Hello world!'
@app.route('/<name>')
def user(name):
mydict = {"key": "This is a Flask Program"}
mylist = ['it', 'Hello', 'the', 'world']
myintvar = 0
myobj = Human()
return render_template('user.html', name=name, mydict=mydict, mylist=mylist, myintvar=myintvar, myobj=myobj)
if __name__ == '__main__':
app.run()
也能夠使用過濾器來修改變量,過濾器名添加在變量名以后,中間使用豎線分隔
過濾器名 | 說明 |
---|---|
safe | 渲染值時不轉義 |
capitalize | 把值的首字母轉換成大寫,其他字母轉換成小寫 |
lower | 把值轉換成小寫情勢 |
upper | 把值轉換成大寫情勢 |
title | 把值中的每一個單詞的首字母都變成大寫 |
trim | 把值的首尾空格去掉 |
striptags | 渲染前把值中的 HTML 標簽都刪掉 |
默許情況出于安全斟酌, Jinja2 會轉義所有變量,完全的過濾器可去 Jinja2 的文檔中查看
Jinja2 提供了多種控制結構,可用來改變模版的渲染流程
{{ % if user %}}
hello, {{ user }}
{{% else %}}
hello, world
{{% endif % }}
是條件控制結構
{% for comment in comments %}
<li>{{ comment }}</li>
{% endfor %}
是循環控制結構, Jinja2 還支持宏,宏類似于 Python 代碼中的函數
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route('/macro')
def macro():
comments = ['it', 'Hello', 'the', 'world']
return render_template('macro.html', comments=comments)
if __name__ == '__main__':
app.run()
在響應模版macro.html
中
{% macro render_comment(comment) %}
<li>{{comment}}</li>
{% endmacro %}
<ul>
{% for comment in comments%}
{{ macro.render_comment(comment) }}
{% endfor %}
</ul>
奇怪的是這個例子我1直返回的是500毛病,沒有成功
為了提高重用性,可以把重復使用的部份保存在單獨的文件中,在需要使用的模版中導入便可
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route('/comments')
def comments():
comments = ["it", "Hello", "the", "world"]
return render_template('comments.html', comments=comments)
if __name__ == '__main__':
app.run()
在macro.html
中
{% macro render_comment(comment) %}
<li>{{comment}}</li>
{% endmacro %}
在comments.html
中
{% import 'macro.html' as macro %}
<ul>
{% for comment in comments%}
{{ macro.render_comment(comment) }}
{% endfor %}
</ul>
提高重用性的更好方式是模版繼承,就像面向對象中的繼承類似,首先創建1個base.html
的基模版
<html>
<head>
{% block head%}
<title>
{% block title%}{% endblock%}- My Application
</title>
{% endblock %}
</head>
<body>
{% block body%}
{% endblock%}
</body>
</html>
衍生模版為child.html
{% extends 'base.html'%}
{% block title%}
Index
{% endblock %}
{% block head%}
{{ super() }}
<style>
</style>
{% endblock%}
{% block body%}
<h1>Helll, World!</h1>
{% endblock%}
在 Python 文件中
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route('/extends')
def extends():
return render_template('child.html')
if __name__ == '__main__':
app.run()
extends
指令聲明這個模版衍生自base.html
,在此指令后基模版的3個塊被重新定義,模版引擎會將其插入適當的位置,使用super()
來獲得原來的內容
from flask.ext.bootstrap import Bootstrap
from flask import Flask
from flask import render_template
app = Flask(__name__)
bootstrap = Bootstrap(app)
@app.route('/bootstrap/<name>')
def bootstrap(name):
return render_template('bootstrap.html', name=name)
if __name__ == '__main__':
app.run()
在bootstrap.html
中
{% extends 'bootstrap/base.html'%}
{% block title%} Flasky {% endblock %}
{% block navbar%}
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">Flasky</a> </div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
</ul>
</div>
</div>
</div>
{% endblock %}
{% block content%}
<div class="container">
<div class="page-header">
<h1>Hello, {{ name }}!</h1> </div>
</div>
{% endblock %}
可以看到,訪問已帶有其他樣式了,這樣就構建了1個“好看”的網頁。Jinja2 中的extends
指令從 Flask-Bootstrap 中導入bootstrap/base.html
,從而實現模版繼承。Flask-Bootstrap 中的基模版提供了1個網頁框架,引入了 Bootstrap 中的所有 CSS 和 JavaScript 文件
Flask-Bootstrap 的基模版中定義了可在衍生模版中重定義的塊,block 和 endblock 指令定義的塊中的內容可添加到基模版中
塊名 | 說明 |
---|---|
doc | 全部 HTML 文檔 |
html_attribs | 標簽中的屬性 |
html | 標簽中的內容 |
head | 標簽中的內容 |
title | 標簽中的內容 |
metas | 1組標簽 |
styles | 層疊樣式表定義 |
body_attribs | 標簽的屬性 |
body | 標簽中的內容 |
navbar | 用戶定義的導航條 |
content | 用戶定義的頁面內容 |
scripts | 文檔底部的 JavaScript 聲明 |
上表中的很多塊都是 Flask-Bootstrap 自用的,最好不要重新定義,Bootstrap 所需的文件在 styles 和 scripts 塊中聲明,如果程序需要向已有內容的塊中添加新內容,必須使用 Jinja2 提供的super()
函數,例如添加1個新的 JavaScript 文件
{% block scripts %}
{{ super() }}
<script type="text/javascript" src="my-script.js"></script>
{% endblock %}
from flask.ext.bootstrap import Bootstrap
from flask import Flask
from flask import render_template
app = Flask(__name__)
bootstrap = Bootstrap(app)
@app.route('/user/<name>')
def bootstrap(name):
return render_template('bootstrap.html', name=name)
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
if __name__ == '__main__':
app.run()
對已有頁面進行復制粘貼的修改肯定可行
不過我們想通過更好的方法(模版繼承)來實現,我們定義1個基模版base.html
{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">Flasky</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
</ul>
</div>
</div>
</div>
{% endblock %}
{% block content %}
<div class="container">
{% block page_content %}{% endblock %}
</div>
{% endblock %}
這個模版的content
塊中只有1個
{% extends 'base.html'%}
{% block title%} 歡迎頁面 {% endblock%}
{% block content %}
<div class="page-header">
<h1> 歡迎,{{ name }}! </h1>
</div>
{% endblock%}
Flask 提供了url_for()
輔助函數,它可使程序 URL 映照中保存的信息生成 URL
url_for()
函數最簡單的用法是以視圖函數名作為參數,返回對應的 URL,
對靜態文件的援用被當做1個特殊的路由,即/static/<filename>
在index.html
中加入以下
{% block head %}
{{ super() }}
<link rel="shortcut icon" href="{{ url_for('static', filename = 'favicon.ico') }}" type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename = 'favicon.ico') }}" type="image/x-icon">
{% endblock %}
在網頁查看源碼可以發現,如果存在/static/<filename>
即會被加載