1. Flask簡介:
flask
是一款非常流行的Python Web
框架,出生于2010年,作者是Armin Ronacher
,本來這個項目只是作者在愚人節的一個玩笑,后來由于非常受歡迎,進而成為一個正式的項目。目前為止最新的版本是0.12.2
。
flask
自2010年發布第一個版本以來,大受歡迎,深得開發者的喜愛,并且在多個公司已經得到了應用,flask能如此流行的原因,可以分為以下幾點:
- 微框架、簡潔、只做他需要做的,給開發者提供了很大的擴展性。
-
Flask
和相關的依賴(Jinja2
、Werkzeug
)設計得非常優秀,用起來很爽。 - 開發效率非常高,比如使用
SQLAlchemy
的ORM
操作數據庫可以節省開發者大量書寫sql
的時間。 - 社會活躍度非常高。
Flask
的靈活度非常之高,他不會幫你做太多的決策,即使做已經幫你做出選擇,你也能非常容易的更換成你需要的,比如:
- 使用
Flask
開發數據庫的時候,具體是使用SQLAlchemy
還是MongoEngine
或者是不用ORM
而直接基于MySQL-Python
這樣的底層驅動進行開發都是可以的,選擇權完全掌握在你自己的手中。區別于Django
,Django
內置了非常完善和豐富的功能,并且如果你想替換成你自己想要的,要么不支持,要么非常麻煩。 - 把默認的
Jinija2
模板引擎替換成Mako
引擎或者是其他模板引擎都是非常容易的。
flask文檔
中文文檔: http://docs.jinkan.org/docs/flask/
英文文檔: http://flask.pocoo.org/docs/0.12/
2. 第一個flask程序:
用pycharm
新建一個flask
項目,新建項目的截圖如下:
點擊
create
后創建一個新項目,然后在helloworld.py
文件中書寫代碼:
#coding: utf8
# 從flask框架中導入Flask類
from flask import Flask
# 傳入__name__初始化一個Flask實例
app = Flask(__name__)
# app.route裝飾器映射URL和執行的函數。這個設置將根URL映射到了hello_world函數上
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
# 運行本項目,host=0.0.0.0可以讓其他電腦也能訪問到該網站,port指定訪問的端口。默認的host是127.0.0.1,port為5000
app.run(host='0.0.0.0',port=9000)
然后點擊運行,在瀏覽器中輸入http://127.0.0.1:9000就能看到hello world
了。需要說明一點的是,app.run
這種方式只適合于開發,如果在生產環境中,應該使用Gunicorn
或者uWSGI
來啟動。如果是在終端運行的,可以按ctrl+c
來讓服務停止。
3. Flask創建app對象
3.1初始化參數
app = Flask()
的可選參數:
import_name
: 導入路徑(尋找靜態目錄與模板目錄位置的參數),一般寫__name__
。
static_url_path
:訪問靜態資源的前綴,默認static
,這個前綴目錄下都是靜態資源。
static_folder
: 靜態目錄的名字,默認static
,其實就是項目中項目目錄存放靜態資源文件夾的名字。
template_folder
: 模板目錄的名字,默認templates
, 其實就是項目中項目目錄存放模板文件文件夾的名字。
3.2 配置參數
有4種,一般使用app.config.from_pyfile
和app.config.from_object
,app.config[key]=value
,app.config.from_envvar
是從虛擬環境中讀取配置參數(一般不用):
- 使用配置文件,項目里新建一個文件,把相關的配置寫到該文件中然后通過
app
對象的config
屬性的from_pyfile
方式來導入配置文件。如在項目目錄中定義配置文件為“config.cfg”
,在程序中引入配置文件方式:app.config.from_pyfile(“config.cfg”)
,路徑可以是相對路徑或者絕對路徑,這種方式,可以傳遞silent=True
,那么這個靜態文件沒有找到的時候,不會拋出異常。
app.config.from_pyfile('config.cfg',silent=True)
# silent=True表示如果配置文件不存在的時候不拋出異常,默認是為False,會拋出異常。
- 從對象中導入,一般是定義一個配置參數的類(類名可以自定義,一般類名是
Config
),再把相關的參數配置以類屬性的方式定義,然后通過app
對象的config
屬性的from_ object
方式來導入。如定義一個Config
類:
class Config(object):
DEBUG = True
導入方式:app.config.from_object(Config)
,可以不用實例化一個Config
對象,因為類也是對象,flask
會自動從Config
類中讀取參數
- 可以通過
app.config[key]=value
,類似于字典的方式直接把相關的配置參數加上。如:
app = Flask(__name__)
app.config['DEBUG'] = True
3.3 在視圖讀取配置參數
app.config.get()
按照字典取值的方法獲取配置參數的值或者是先導入current_app
再用current_app.config.get()
按照字典取值的方法獲取配置參數的值
3.4 app.run的參數
啟動app可以傳參數,比如host
、port
,debug
,如果是配置參數,配置參數只能傳debug
app.run(host="0.0.0.0", port=5000,debug=True)
4. debug模式
4.1 為什么需要開啟DEBUG模式
- 如果開啟了
DEBUG
模式,那么在代碼中如果拋出了異常,在瀏覽器的頁面中可以看到具體的錯誤信息,以及具體的錯誤代碼位置。方便開發者調試。 - 如果開啟了
DEBUG
模式,那么以后在Python
代碼中修改了任何代碼,只要按ctrl+s
,flask
就會自動的重新記載整個網站。不需要手動點擊重新運行。
4.2 debug配置的5種方式
使用配置文件,項目里新建一個文件,把相關的配置寫到該文件中然后通過
app
對象的config
屬性的from_pyfile
方式來導入配置文件。如在項目目錄中定義配置文件為“config.cfg”
,在程序中引入配置文件方式:app.config.from_pyfile(“config.cfg”)
,路徑可以是相對路徑或者絕對路徑,這種方式,可以傳遞silent=True
,那么這個靜態文件沒有找到的時候,不會拋出異常。從對象中導入,一般是定義一個配置參數的類(類名可以自定義,一般類名是
Config
),再把相關的參數配置以類屬性的方式定義,然后通過app
對象的config
屬性的from_ object
方式來導入。如定義一個Config
類:
class Config(object):
DEBUG = True
導入方式:app.config.from_object(Config)
,可以不用實例化一個Config
對象,因為類也是對象,flask
會自動從Config
類中讀取參數。
可以通過
app.config[key]=value
,類似于字典的方式直接把相關的配置參數加上。如app.config["DEBUG"]=True
直接通過
app.debug = True
的方式通過給
app.config
添加參數的方式(類似于添加字典),app.config.update(DEBUG = True)
4.3 PIN碼
開啟了debug
模式后,flask會自動生成一個PIN
碼,PIN
碼用于頁面調試如果想要在網頁上調試代碼,那么應該輸入PIN
碼。
5. url詳解
5.1 在局域網中讓其他電腦訪問我的網站
如果想在同一個局域網下的其他電腦訪問自己電腦上的Flask
網站,那么可以在app.run()
中設置host='0.0.0.0'
才能訪問得到。
5.2 指定端口號
Flask
項目,默認使用5000
端口。如果想更換端口,那么可以在app.run()
中設置port=9000
5.3 url唯一
在定義url
的時候,一定要記得在最后加一個斜杠。
- 如果不加斜杠,那么在瀏覽器中訪問這個url的時候,如果最后加了斜杠,那么就訪問不到。這樣用戶體驗不太好。
- 搜索引擎會將不加斜杠的和加斜杠的視為兩個不同的
url
。而其實加和不加斜杠的都是同一個url
,那么就會給搜索引擎造成一個誤解。加了斜杠,就不會出現沒有斜杠的情況。
5.4 GET請求和POST請求
在網絡請求中有許多請求方式,比如:GET、POST、DELETE、PUT
請求等。那么最常用的就是GET
和POST
請求了。
GET
請求:只會在服務器上獲取資源,不會更改服務器的狀態。這種請求方式推薦使用GET
請求。POST
請求:會給服務器提交一些數據或者文件。一般POST
請求是會對服務器的狀態產生影響,那么這種請求推薦使用POST
請求。-
關于參數傳遞:
-
GET
請求:把參數放到url
中,通過?xx=xxx
的形式傳遞的。因為會把參數放到url中,所以如果視力好,一眼就能看到你傳遞給服務器的參數。這樣不太安全。 -
POST
請求:把參數放到Form Data
中。會把參數放到Form Data
中,避免了被偷瞄的風險,但是如果別人想要偷看你的密碼,那么其實可以通過抓包的形式。因為POST
請求可以提交一些數據給服務器,比如可以發送文件,那么這就增加了很大的風險。所以POST
請求,對于那些有經驗的黑客來講,其實是更不安全的。
-
在
Flask
中,route
方法,默認將只能使用GET
的方式請求這個url
,如果想要設置自己的請求方式,那么應該傳遞一個methods
參數來限制訪問方式。
@app.route('/sample', methods=['GET', 'POST'])
def my_list(page):
return 'my list'
5.5 url與函數的映射
從之前的helloworld.py
文件中,我們已經看到,一個URL要與執行函數進行映射,使用的是@app.route
裝飾器。@app.route
裝飾器中,可以指定URL的規則來進行更加詳細的映射,比如現在要映射一個文章詳情的URL,文章詳情的URL是/article/id/,id有可能為1、2、3...,那么可以通過以下方式:
@app.route('/article/<id>/')
def article(id):
return '%s article detail' % id
其中<id>,尖括號是固定寫法,語法為<variable_name>
,variable_name
默認的數據類型是字符串。如果需要指定類型,則要寫成<converter:variable_name>
,其中converter
就是類型名稱,可以有以下幾種:
string
: 默認的數據類型,接受沒有任何斜杠“/”的文本。
int
: 接受整形。
float
: 接受浮點類型。
path
: 和string的類似,但是接受斜杠。
uuid
: 只接受uuid字符串。
any
:可以指定多種路徑,這個通過一個例子來進行說明:
@app.route('/<any(article,blog):url_path>/')
def item(url_path):
return url_path
以上例子中,item
這個函數可以接受兩個URL,一個是/article/,另一個是/blog/。并且,一定要傳url_path
參數,當然這個url_path
的名稱可以隨便。
如果不想定制子路徑來傳遞參數,也可以通過傳統的?=的形式來傳遞參數,例如:/article?id=xxx,這種情況下,可以通過request.args.get('id')
來獲取id
的值。如果是post
方法,則可以通過request.form.get('id')
來進行獲取。
6. url_for詳解
6.1 url_for的基本使用
url_for
第一個參數,應該是視圖函數的名字的字符串。后面的參數就是傳遞給url
。如果傳遞的參數之前在url
中已經定義了,那么這個參數就會被當成path
的形式給url
。如果這個參數之前沒有在url
中定義,那么將變成查詢字符串的形式放到url
中。
@app.route('/post/list/<page>/')
def my_list(page):
return 'my list'
print(url_for('my_list',page=1,count=2))
# 構建出來的url:/my_list/1/?count=2
6.2 為什么需要url_for
- 將來如果修改了
URL
,但沒有修改該URL對應的函數名,就不用到處去替換URL了。 -
url_for
會自動的處理那些特殊的字符,不需要手動去處理。 - 強烈建議以后在使用url的時候,使用
url_for
來反轉url。
url = url_for('login',next='/')
# 會自動的將/編碼,不需要手動去處理。
# url=/login/?next=%2F
7. 動態路由
7.1 傳遞參數
傳遞參數的語法是:/<參數名>/
。然后在視圖函數中,也要定義同名的參數。
7.2 參數的數據類型
- 如果沒有指定具體的數據類型,那么默認就是使用
string
數據類型。 -
int
數據類型只能傳遞int
類型。 -
float
數據類型只能傳遞float
類型。 -
path
數據類型和string
有點類似,都是可以接收任意的字符串,但是path
可以接收路徑,也就是說可以包含斜杠。 -
uuid
數據類型只能接收符合uuid
的字符串。uuid
是一個全宇宙都唯一的字符串,一般可以用來作為表的主鍵。 -
any
數據類型可以在一個url
中指定多個路徑。例如:@app.route('/<any(blog,article):url_path>/<id>/') def detail(url_path,id): if url_path == 'blog': return '博客詳情:%s' % id else: return '博客詳情:%s' % id # 路由傳遞的參數默認當做string處理,這里指定int,尖括號中冒號后面的內容是動態的 @app.route('/user/<int:id>') def hello_itcast(id): return 'hello word %d' %id
7.3 接收用戶傳遞的參數
- 第一種:使用path的形式(將參數嵌入到路徑中),就是上面講的。
- 第二種:使用查詢字符串的方式,就是通過
?key=value
的形式傳遞的。@app.route('/d/') def d(): wd = request.args.get('wd') return '您通過查詢字符串的方式傳遞的參數是:%s' % wd
- 如果你的這個頁面的想要做
SEO
優化,就是被搜索引擎搜索到,那么推薦使用第一種形式(path的形式)。如果不在乎搜索引擎優化,那么就可以使用第二種(查詢字符串的形式)。
7.4 app.url_map`查看所有路由
通過app.url_map
可以查看整個flask
中的路由信息
7.5 同一路由裝飾多個視圖函數
如果兩個路由的請求方式相同,那么先定義的視圖函數將會覆后定義的視圖函數,如果不同則根據請求方式來調用視圖
@app.route('/index')
def index():
return "hello"
@app.route('/index')
def index1():
return "hello1"
上面這種情況,在瀏覽器訪問index,只會執行第一個視圖函數,第二個視圖函數將會被覆蓋
@app.route('/index', methods=['POST'])
def index():
return "hello"
@app.route('/index', methods=['GET'])
def index1():
return "hello1"
上面這種方式,會根據請求方式來決定執行那個視圖
7.6 同一視圖多個路由裝飾器
裝飾器層疊就可以
@app.route('/index')
@app.route('/index1')
def index():
return "hello"
8. 自定義URL轉換器
8.1 自定義URL轉換器的方式
- 轉換器是一個類,且必須繼承自
werkzeug.routing.BaseConverter
。 - 在轉換器類中,實現
to_python(self,value)
方法,這個方法的返回值,將會傳遞到view
函數中作為參數。 - 在轉換器類中,實現
to_url(self,values)
方法,這個方法的返回值,將會在調用url_for
函數的時候生成符合要求的URL形式。 - 在自定義的類中,重寫
regex
,也就是這個變量的正則表達式。 - 將自定義的類,映射到
app.url_map.converters
上。比如:app.url_map.converters['tel'] = TelephoneConverter
8.2 to_python的作用
這個方法的返回值,將會傳遞到view
函數中作為參數。
8.3 to_url
的作用
這個方法的返回值,將會在調用url_for
函數的時候生成符合要求的url
形式。
8.4 萬能轉換器
可以定義一個正則的萬能表達式,可以根據傳入的正則表達式提取相關參數:
from flask import Flask
from werkzeug.routing import BaseConverter
class Regex_url(BaseConverter):
def __init__(self,url_map,*args):
super(Regex_url,self).__init__(url_map)
self.regex = args[0]
app = Flask(__name__)
app.url_map.converters['re'] = Regex_url
@app.route('/user/<re("[a-z]{3}"):id>')
def hello_itcast(id):
return 'hello %s' %id
9. redirect重定向
9.1 重定向分類
重定向分為永久性重定向和暫時性重定向,在頁面上體現的操作就是瀏覽器會從一個頁面自動跳轉到另外一個頁面。比如用戶訪問了一個需要權限的頁面,但是該用戶當前并沒有登錄,因此我們應該給他重定向到登錄頁面。
永久性重定向:
http
的狀態碼是301
,多用于舊網址被廢棄了要轉到一個新的網址確保用戶的訪問,最經典的就是京東網站,你輸入www.jingdong.com
的時候,會被重定向到www.jd.com
,因為jingdong.com
這個網址已經被廢棄了,被改成jd.com
,所以這種情況下應該用永久重定向。暫時性重定向:
http
的狀態碼是302
,表示頁面的暫時性跳轉。比如訪問一個需要權限的網址,如果當前用戶沒有登錄,應該重定向到登錄頁面,這種情況下,應該用暫時性重定向。
9.2 flask中重定向
flask
中有一個函數叫做redirect
,可以重定向到指定的頁面。示例代碼如下:
# 先導入redirect
from flask import Flask,request,redirect,url_for
app = Flask(__name__)
@app.route('/login/')
def login():
return '這是登錄頁面'
@app.route('/profile/')
def profile():
if request.args.get('name'):
return '個人中心頁面'
else:
# redirect 重定向
return redirect(url_for('login'))
10. response詳解
10.1 視圖函數中可以返回哪些值
- 可以返回字符串:返回的字符串其實底層將這個字符串包裝成了一個
Response
對象。 - 可以返回元組:這樣的元組必須是
(response, status, headers)
的形式,且至少包含一個元素。status
值會覆蓋狀態代碼,headers
可以是一個列表或字典,作為額外的消息標頭值。元組的形式是(響應體,狀態碼,頭部信息),也不一定三個都要寫,寫兩個也是可以的。返回的元組,其實在底層也是包裝成了一個Response
對象。 - 可以返回
Response
及其子類。
10.2 實現一個自定義的Response對象
- 繼承自
Response
類。 - 實現方法
force_type(cls,rv,environ=None)
。 - 指定
app.response_class
為你自定義的Response
對象。 - 如果視圖函數返回的數據,不是字符串,也不是元組,也不是Response對象,那么就會將返回值傳給
force_type
,然后再將force_type
的返回值返回給前端。
from flask import Flask,Response,jsonify,render_template
# flask = werkzeug+sqlalchemy+jinja2
import json
app = Flask(__name__)
# 將視圖函數中返回的字典,轉換成json對象,然后返回
# restful-api
class JSONResponse(Response):
@classmethod
def force_type(cls, response, environ=None):
"""
這個方法只有視圖函數返回非字符、非元組、非Response對象
才會調用
response:視圖函數的返回值
"""
if isinstance(response,dict):
# jsonify除了將字典轉換成json對象,還將改對象包裝成了一個Response對象
response = jsonify(response)
return super(JSONResponse, cls).force_type(response,environ)
app.response_class = JSONResponse
@app.route('/')
def hello_world():
# Response('Hello World!',status=200,mimetype='text/html')
return 'Hello World!'
@app.route('/list1/')
def list1():
resp = Response('list1')
resp.set_cookie('country','china')
return resp
@app.route('/list2/')
def list2():
return 'list2',200,{'X-NAME':'zhiliao'}
@app.route('/list3/')
def list3():
return {'username':'zhiliao','age':18}
if __name__ == '__main__':
app.run(debug=True,port=8000)
10.3 abort函數
-
abort()
: 立即停止視圖函數的執行,并且把相對應的信息返回到前端中使用abort函數可以立即終止視圖函數的執行 并可以返回給前端特定的信息abort()
函數可以傳遞兩種方式的信息,(1) ①:一種是傳遞狀態碼信息,例如:
abort(404)
,前端就會報出已經默認好的404錯誤信息
②:自定義錯誤信息,(用裝飾器)定義一個視圖函數這個函數的返回值會是前端所看到的最終的結果,這個視圖函數的裝飾器來定義錯誤的類型@app.errorhandler(404) # 定義404的錯誤類型
自定義的錯誤類型,要和
abort()
里面定義的錯誤類型一致(2) 另外一種方式是傳遞響應體的信息,
abort(Response("內容"))
,Response()
,里面的信息會在前端上面顯示出來代碼:404錯誤...加上自定義的錯誤信息如果使用傳遞響應體信息的話就會只有響應體信息的錯誤異常
from flask import Flask, request, abort,Response
app = Flask(__name__)
@app.route("/login", methods=["GET"])
def login():
name = ""
pwd = ""
if name != "zhangsan" or pwd != "admin":
"""
使用abort函數可以立即終止視圖函數的執行
并可以返回給前端特定的信息
1. 傳遞狀態碼信息,必須是標準的http狀態碼
可以接受狀態碼信息,返回給前端已經默認好的信息
"""
abort(404)
"""
2. 傳遞響應體的信息, Response()里面的內容會在前端上顯示出來
resp = Response("login failed")
abort(resp)
"""
return "login success"
"""自定義異常處理"""
@app.errorhandler(404)
def handle_404_error(error): # 接受一個錯誤信息
"""自定義的處理404錯誤方法"""
'''這個函數的返回值會是前端所看到的最終的結果'''
return "出現了404錯誤,錯誤信息:%s"%error
if __name__ == '__main__':
app.run(debug=True)
10.4 自定義異常處理
自定義錯誤信息,(用裝飾器)定義一個視圖函數這個函數的返回值會是前端所看到的最終的結果,這個視圖函數的裝飾器來定義錯誤的類型
@app.errorhandler(404)
def error(e):
return '您請求的頁面不存在了,請確認后再次訪問!%s'%e
10.5 make_response()詳解
make_response()
,相當于DJango
中的HttpResponse
。
1.返回內容
from flask import make_response
@blue.route('/makeresponse/')
def make_response_function():
response = make_response('<h2>羞羞噠</h2>')
return response, 404
2.返回頁面
from flask import make_response
@blue.route('/makeresponse/')
def make_response_function():
temp = render_template('hello.html')
response = make_response(temp)
return response
- 注意:
make_response
想要返回頁面,不能直接寫做:make_response('hello.html')
,必須用render_template('hello.html')
形式。
3.返回狀態碼
- 方式一:在
make_response()
中傳入狀態碼
from flask import make_response
@blue.route('/makeresponse/')
def make_response_function():
temp = render_template('hello.html')
response = make_response(temp, 200)
return response
- 方式二:直接
return
狀態碼
from flask import make_response
@blue.route('/makeresponse/')
def make_response_function():
temp = render_template('hello.html')
response = make_response(temp)
return response, 200
11. 獲取請求參數
11.1 request詳解
獲取請求參數時首先要導入request
模塊
from flask import request
就是 Flask
中表示當前請求的request
對象,request
對象中保存了一次HTTP
請求的一切信息。
11.2 request的常用屬性
-
methods
默認的請求方式只有GET
,其他請求都需要通過參數methods
進行指定。
methods=['GET', 'POST']
-
args
:獲取GET請求參數
flask中,要獲取get
請求的參數,不是通過request.GET.get()
,而是通過request.args.get()
獲取。
request.agrs.get('參數名')
-
form
:獲取POST請求參數
flask中,要獲取get
請求的參數,不是通過request.POST.get()
,而是通過request.form.get()
獲取。
request.form.get('參數名')
11.3 上傳文件
已上傳的文件存儲在內存或是文件系統中一個臨時的位置。你可以通過請求對象的files
屬性訪問它們。每個上傳的文件都會存儲在這個字典里。它表現近乎為一個標準的 Python file
對象,但它還有一個 save()
方法,這個方法允許你把文件保存到服務器的文件系統上。這里是一個用它保存文件的例子:
from flask import request
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/uploaded_file.txt')
...
如果你想知道上傳前文件在客戶端的文件名是什么,你可以訪問filename
屬性。但請記住, 永遠不要信任這個值,這個值是可以偽造的。如果你要把文件按客戶端提供的文件名存儲在服務器上,那么請把它傳遞給Werkzeug
提供的secure_filename()
函數:
from flask import request
from werkzeug import secure_filename
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/' + secure_filename(f.filename))