微框架、簡(jiǎn)潔、只做他需要做的,給開發(fā)展提供了很大的擴(kuò)展性。 Flask和相關(guān)的依賴(Jinja2、Werkzeug)設(shè)計(jì)得非常優(yōu)秀,用起來很爽。 開發(fā)效率非常高,比如使用SQLAlchemy的ORM操作數(shù)據(jù)庫可以節(jié)省開發(fā)者大量書寫sql的時(shí)間。 社會(huì)活躍度非常高。 Flask的靈活度非常之高,他不會(huì)幫你做太多的決策,即使做已經(jīng)幫你做出選擇,你也能非常容易的更換成你需要的,比如: 使用Flask開發(fā)數(shù)據(jù)庫的時(shí)候,具體是使用SQLAlchemy還是MongoEngine或者是不用ORM而直接基于MySQL-Python這樣的底層驅(qū)動(dòng)進(jìn)行開發(fā)都是可以的,選擇權(quán)完全掌握在你自己的手中。區(qū)別于Django,Django內(nèi)置了非常完善和豐富的功能,并且如果你想替換成你自己想要的,要么不支持,要么非常麻煩。 把默認(rèn)的Jinija2模板引擎替換成Mako引擎或者是其他模板引擎都是非常容易的。 第一個(gè)flask程序: 用pycharm新建一個(gè)flask項(xiàng)目,新建項(xiàng)目的截圖如下: 點(diǎn)擊create后創(chuàng)建一個(gè)新項(xiàng)目,然后在helloworld.py文件中書寫代碼: #coding: utf8 # 從flask框架中導(dǎo)入Flask類 from flask import Flask # 傳入__name__初始化一個(gè)Flask實(shí)例 app = Flask(__name__) # app.route裝飾器映射URL和執(zhí)行的函數(shù)。這個(gè)設(shè)置將根URL映射到了hello_world函數(shù)上 @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': # 運(yùn)行本項(xiàng)目,host=0.0.0.0可以讓其他電腦也能訪問到該網(wǎng)站,port指定訪問的端口。默認(rèn)的host是127.0.0.1,port為5000 app.run(host='0.0.0.0',port=9000) 然后點(diǎn)擊運(yùn)行,在瀏覽器中輸入http://127.0.0.1:9000就能看到hello world了。需要說明一點(diǎn)的是,app.run這種方式只適合于開發(fā),如果在生產(chǎn)環(huán)境中,應(yīng)該使用Gunicorn或者uWSGI來啟動(dòng)。如果是在終端運(yùn)行的,可以按ctrl+c來讓服務(wù)停止。 設(shè)置為DEBUG模式: 默認(rèn)情況下flask不會(huì)開啟DEBUG模式,開啟DEBUG模式后,flask會(huì)在每次保存代碼的時(shí)候自動(dòng)的重新載入代碼,并且如果代碼有錯(cuò)誤,會(huì)在終端進(jìn)行提示。 開啟DEBUG模式有三種方式: 直接在應(yīng)用對(duì)象上設(shè)置: app.debug = True app.run() 在執(zhí)行run方法的時(shí)候,傳遞參數(shù)進(jìn)去: app.run(debug=True) 在config屬性中設(shè)置: app.config.update(DEBUG=True) 如果一切正常,會(huì)在終端打印以下信息: * Restarting with stat * Debugger is active! * Debugger pin code: 294-745-044 * Running on http://0.0.0.0:9000/ (Press CTRL+C to quit) 需要注意的是,只能在開發(fā)環(huán)境下開啟DEBUG模式,因?yàn)镈EBUG模式會(huì)帶來非常大的安全隱患。 另外,在開啟了DEBUG模式后,當(dāng)程序有異常而進(jìn)入錯(cuò)誤堆棧模式,你第一次點(diǎn)擊某個(gè)堆棧想查看變量值的時(shí)候,頁面會(huì)彈出一個(gè)對(duì)話框,讓你輸入PIN值,這個(gè)PIN值在你啟動(dòng)的時(shí)候就會(huì)出現(xiàn),比如在剛剛啟動(dòng)的項(xiàng)目中的PIN值為294-745-044,你輸入這個(gè)值后,Werkzeug會(huì)把這個(gè)PIN值作為cookie的一部分保存起來,并在8小時(shí)候過期,8小時(shí)以內(nèi)不需要再輸入PIN值。這樣做的目的是為了更加的安全,讓調(diào)試模式下的攻擊者更難攻擊到本站。 項(xiàng)目配置: Flask項(xiàng)目的配置,都是通過app.config對(duì)象來進(jìn)行配置的。比如要配置一個(gè)項(xiàng)目處于DEBUG模式下,那么可以使用app.config['DEBUG] = True來進(jìn)行設(shè)置,那么Flask項(xiàng)目將以DEBUG模式運(yùn)行。在Flask項(xiàng)目中,有四種方式進(jìn)行項(xiàng)目的配置: 直接硬編碼: app = Flask(__name__) app.config['DEBUG'] = True 因?yàn)閍pp.config是flask.config.Config的實(shí)例,而Config類是繼承自dict,因此可以通過update方法: app.config.update( DEBUG=True, SECRET_KEY='...' ) 如果你的配置項(xiàng)特別多,你可以把所有的配置項(xiàng)都放在一個(gè)模塊中,然后通過加載模塊的方式進(jìn)行配置,假設(shè)有一個(gè)settings.py模塊,專門用來存儲(chǔ)配置項(xiàng)的,此時(shí)你可以通過app.config.from_object()方法進(jìn)行加載,并且該方法既可以接收模塊的的字符串名稱,也可以模塊對(duì)象: # 1. 通過模塊字符串 app.config.from_object('settings') # 2. 通過模塊對(duì)象 import settings app.config.from_object(settings) 也可以通過另外一個(gè)方法加載,該方法就是app.config.from_pyfile(),該方法傳入一個(gè)文件名,通常是以.py結(jié)尾的文件,但也不限于只使用.py后綴的文件: app.config.from_pyfile('settings.py',silent=True) # silent=True表示如果配置文件不存在的時(shí)候不拋出異常,默認(rèn)是為False,會(huì)拋出異常。 Flask項(xiàng)目?jī)?nèi)置了許多的配置項(xiàng),所有的內(nèi)置配置項(xiàng),可以在這里查看。 URL與函數(shù)的映射: 從之前的helloworld.py文件中,我們已經(jīng)看到,一個(gè)URL要與執(zhí)行函數(shù)進(jìn)行映射,使用的是@app.route裝飾器。@app.route裝飾器中,可以指定URL的規(guī)則來進(jìn)行更加詳細(xì)的映射,比如現(xiàn)在要映射一個(gè)文章詳情的URL,文章詳情的URL是/article/id/,id有可能為1、2、3...,那么可以通過以下方式: python @app.route('/article/ string: 默認(rèn)的數(shù)據(jù)類型,接受沒有任何斜杠“/”的文本。 int: 接受整形。 float: 接受浮點(diǎn)類型。 path: 和string的類似,但是接受斜杠。 uuid: 只接受uuid字符串。 any:可以指定多種路徑,這個(gè)通過一個(gè)例子來進(jìn)行說明: @app.route('/ def item(url_path): return url_path 以上例子中,item這個(gè)函數(shù)可以接受兩個(gè)URL,一個(gè)是/article/,另一個(gè)是/blog/。并且,一定要傳url_path參數(shù),當(dāng)然這個(gè)url_path的名稱可以隨便。 如果不想定制子路徑來傳遞參數(shù),也可以通過傳統(tǒng)的?=的形式來傳遞參數(shù),例如:/article?id=xxx,這種情況下,可以通過request.args.get('id')來獲取id的值。如果是post方法,則可以通過request.form.get('id')來進(jìn)行獲取。 構(gòu)造URL(url_for): 一般我們通過一個(gè)URL就可以執(zhí)行到某一個(gè)函數(shù)。如果反過來,我們知道一個(gè)函數(shù),怎么去獲得這個(gè)URL呢?url_for函數(shù)就可以幫我們實(shí)現(xiàn)這個(gè)功能。url_for()函數(shù)接收兩個(gè)及以上的參數(shù),他接收函數(shù)名作為第一個(gè)參數(shù),接收對(duì)應(yīng)URL規(guī)則的命名參數(shù),如果還出現(xiàn)其他的參數(shù),則會(huì)添加到URL的后面作為查詢參數(shù)。 通過構(gòu)建URL的方式而選擇直接在代碼中拼URL的原因有兩點(diǎn): 將來如果修改了URL,但沒有修改該URL對(duì)應(yīng)的函數(shù)名,就不用到處去替換URL了。 url_for()函數(shù)會(huì)轉(zhuǎn)義特殊字符和Unocode數(shù)據(jù),這些工作都不需要我們自己處理。 下面用一個(gè)例子來進(jìn)行解釋: ```python from flask import Flask,url_for app = Flask(name) @app.route('/article/ def article(id): return '%s article detail' % id # 這行的代碼可以在交互模式下產(chǎn)生請(qǐng)求上下文,不用`app.run()`來運(yùn)行這個(gè)項(xiàng)目,直接可以運(yùn)行下面的代碼, # 也會(huì)有`flask`上下文 with app.test_request_context(): print url_for('article',id='1') print url_for('article',id='2',next='/') ``` 執(zhí)行后的結(jié)果如下: > /article/1/ > /article/2/?next=%2F 自定義URL轉(zhuǎn)換器: 剛剛在URL映射的時(shí)候,我們看到了Flask內(nèi)置了幾種數(shù)據(jù)類型的轉(zhuǎn)換器,比如有int/string等。如果Flask內(nèi)置的轉(zhuǎn)換器不能滿足你的需求,此時(shí)你可以自定義轉(zhuǎn)換器。自定義轉(zhuǎn)換器,需要滿足以下幾個(gè)條件: 轉(zhuǎn)換器是一個(gè)類,且必須繼承自werkzeug.routing.BaseConverter。 在轉(zhuǎn)換器類中,必須實(shí)現(xiàn)to_python(self,value)方法,這個(gè)方法的返回值,將會(huì)傳遞到view函數(shù)中作為參數(shù)。 在轉(zhuǎn)換器類中,必須實(shí)現(xiàn)to_url(self,values)方法,這個(gè)方法的返回值,將會(huì)在調(diào)用url_for函數(shù)的時(shí)候生成符合要求的URL形式。 比如,拿一個(gè)官方的例子來說,Reddit可以通過在URL中用一個(gè)加號(hào)(+)隔開社區(qū)的名字,方便同時(shí)查看來自多個(gè)社區(qū)的帖子。比如訪問“www.reddit.com/r/flask+lisp/”的時(shí)候,就同時(shí)可以查看flask和lisp兩個(gè)社區(qū)的帖子,現(xiàn)在我們自定義一個(gè)轉(zhuǎn)換器來實(shí)現(xiàn)這個(gè)功能: #coding: utf8 from flask import Flask,url_for from werkzeug.routing import BaseConverter class ListConverter(BaseConverter): def __init__(self,url_map,separator='+'): super(ListConverter,self).__init__(url_map) self.separator = separator def to_python(self, value): return value.split(self.separator) def to_url(self, values): return self.separator.join(BaseConverter.to_url(self,value) for value in values) @app.route('/community1/ def community1(page_names): return '%s+%s' % tuple(page_names) @app.route('/community2/ def community2(page_names): return '%s|%s' % tuple(page_names) communityu1使用的是默認(rèn)的+號(hào)進(jìn)行連接,而第二種方式使用了|進(jìn)行連接。 URL唯一: Flask的URL規(guī)則是基于Werkzeug的路由模塊。這個(gè)模塊的思想是基于Apache以及更早的HTTP服務(wù)器的主張,希望保證優(yōu)雅且唯一的URL。 舉個(gè)例子: @app.route('/projects/') def projects(): return 'project page' 上述例子中,當(dāng)訪問一個(gè)結(jié)尾不帶斜線的URL會(huì)被重定向到帶斜線的URL上去。這樣有助于避免搜索引擎搜索同一個(gè)頁面兩次。 再看一個(gè)例子: @app.route('/about') def about(): return 'about page' 以上例子中,當(dāng)訪問帶斜線的URL(/about/)會(huì)產(chǎn)生一個(gè)404('Not Found')錯(cuò)誤。 指定HTTP方法: 在@app.route()中可以傳入一個(gè)關(guān)鍵字參數(shù)methods來指定本方法支持的HTTP方法,默認(rèn)只響應(yīng)GET請(qǐng)求,看以下例子: python @app.route('/login/',methods=['GET','POST']) def login(): return 'login' 以上裝飾器將讓login的URL既能支持GET又能支持POST。 頁面跳轉(zhuǎn)和重定向: 重定向分為永久性重定向和暫時(shí)性重定向,在頁面上體現(xiàn)的操作就是瀏覽器會(huì)從一個(gè)頁面自動(dòng)跳轉(zhuǎn)到另外一個(gè)頁面。比如用戶訪問了一個(gè)需要權(quán)限的頁面,但是該用戶當(dāng)前并沒有登錄,因此我們應(yīng)該給他重定向到登錄頁面。 永久性重定向: http的狀態(tài)碼是301,多用于舊網(wǎng)址被廢棄了要轉(zhuǎn)到一個(gè)新的網(wǎng)址確保用戶的訪問,最經(jīng)典的就是京東網(wǎng)站,你輸入www.jingdong.com的時(shí)候,會(huì)被重定向到www.jd.com,因?yàn)閖ingdong.com這個(gè)網(wǎng)址已經(jīng)被廢棄了,被改成jd.com,所以這種情況下應(yīng)該用永久重定向。 暫時(shí)性重定向: http的狀態(tài)碼是302,表示頁面的暫時(shí)性跳轉(zhuǎn)。比如訪問一個(gè)需要權(quán)限的網(wǎng)址,如果當(dāng)前用戶沒有登錄,應(yīng)該重定向到登錄頁面,這種情況下,應(yīng)該用暫時(shí)性重定向。 在flask中,重定向是通過flask.redirect(location,code=302)這個(gè)函數(shù)來實(shí)現(xiàn)的,location表示需要重定向到的URL,應(yīng)該配合之前講的url_for()函數(shù)來使用,code表示采用哪個(gè)重定向,默認(rèn)是302也即暫時(shí)性重定向,可以修改成301來實(shí)現(xiàn)永久性重定向。 以下來看一個(gè)例子,關(guān)于在flask中怎么使用重定向: from flask import Flask,url_for,redirect app = Flask(__name__) app.debug = True @app.route('/login/',methods=['GET','POST']) def login(): return 'login page' @app.route('/profile/',methods=['GET','POST']) def profile(): name = request.args.get('name') if not name: # 如果沒有name,說明沒有登錄,重定向到登錄頁面 return redirect() else: return name 關(guān)于響應(yīng)(Response): 視圖函數(shù)的返回值會(huì)被自動(dòng)轉(zhuǎn)換為一個(gè)響應(yīng)對(duì)象,F(xiàn)lask的轉(zhuǎn)換邏輯如下: 如果返回的是一個(gè)合法的響應(yīng)對(duì)象,則直接返回。 如果返回的是一個(gè)字符串,那么Flask會(huì)重新創(chuàng)建一個(gè)werkzeug.wrappers.Response對(duì)象,Response將該字符串作為主體,狀態(tài)碼為200,MIME類型為text/html,然后返回該Response對(duì)象。 如果返回的是一個(gè)元組,元祖中的數(shù)據(jù)類型是(response,status,headers),只能包含一個(gè)元素。status值會(huì)覆蓋默認(rèn)的200狀態(tài)碼,headers可以是一個(gè)列表或者字典,作為額外的消息頭。 如果以上條件都不滿足,F(xiàn)lask會(huì)假設(shè)返回值是一個(gè)合法的WSGIt應(yīng)用程序,并通過Response.force_type(rv,request.environ)轉(zhuǎn)換為一個(gè)請(qǐng)求對(duì)象。 以下將用例子來進(jìn)行說明: 第一個(gè)例子:直接使用Response創(chuàng)建: from werkzeug.wrappers import Response @app.route('/about/') def about(): resp = Response(response='about page',status=200,content_type='text/html;charset=utf-8') return resp 第二個(gè)例子:可以使用make_response函數(shù)來創(chuàng)建Response對(duì)象,這個(gè)更加的方便,因?yàn)樗庋b了默認(rèn)的Content-Type以及status等: from flask import make_response @app.route('/about/') def about(): return make_response('about page') 第三個(gè)例子:通過返回元組的形式: @app.errorhandler(404) def not_found(): return 'not found',404 第四個(gè)例子:自定義響應(yīng)。自定義響應(yīng)必須滿足三個(gè)條件: 必須繼承自Response類。 必須實(shí)現(xiàn)類方法force_type(cls,rv,environ=None)。 必須指定app.response_class為你自定義的Response 以下將用一個(gè)例子來進(jìn)行講解,Restful API都是通過JSON的形式進(jìn)行傳遞,如果你的后臺(tái)跟前臺(tái)進(jìn)行交互,所有的URL都是發(fā)送JSON數(shù)據(jù),那么此時(shí)你可以自定義一個(gè)叫做JSONResponse的類來代替Flask自帶的Response類: from flask import Flask,jsonify from werkzeug.wrappers import Response app = Flask(__name__) class JSONResponse(Response): default_mimetype = 'application/json' @classmethod def force_type(cls,response,environ=None): if isinstance(response,dict): response = jsonify(response) return super(JSONResponse,cls).force_type(response,environ) app.response_class = JSONResponse @app.route('/about/') def about(): return {'message':'about page'} if __name__ == '__main__': app.run(host='0.0.0.0',port=8000) 此時(shí)如果你訪問/about/這個(gè)URL,那么在頁面中將會(huì)顯示: { 'message': 'about page' } |
|