Flask是一個基於Python開發而且依賴jinja2模板和Werkzeug WSGI服務的一個微型框架,對於Werkzeug本質是Socket服務端,其用於接收http請求並對請求進行預處理,而後觸發Flask框架,開發人員基於Flask框架提供的功能對請求進行相應的處理,並返回給用戶,若是要返回給用戶複雜的內容時,須要藉助jinja2模板來實現對模板的處理,即:將模板和數據進行渲染,將渲染後的字符串返回給用戶瀏覽器。html
本篇文章將對比與django介紹flask的基本組件以及相關使用方法。前端
django:無socket、依賴第三方模塊wsgi、中間件、路由系統、視圖、ORM、cookie、session、Admin、Form、緩存、信號、序列化。python
Flask: 無socket、中間件(擴展)、路由系統、視圖(第三方模塊,依賴jinja2)、cookie、session。mysql
pip3 install flask
#!/usr/bin/env python3 #_*_ coding:utf-8 _*_ #Author:wd from flask import Flask app = Flask(__name__) # 實例化Flask對象 @app.route('/') #添加路由 def hello_world(): return 'Hello World!' # 返回 if __name__ == '__main__': app.run() # 運行服務器
flask的配置文件是經過Flask對象的config.Configs進行配置,本質是字典其中配置文件有如下實現方式:正則表達式
方式一: app.config['DEBUG'] = True # PS: 因爲Config對象本質上是字典,因此還可使用app.config.update(...) 方式二: app.config.from_pyfile("python文件名稱") 如: settings.py DEBUG = True app.config.from_pyfile("settings.py") app.config.from_envvar("環境變量名稱")#環境變量的值爲python文件名稱名稱,內部調用from_pyfile方法 app.config.from_json("json文件名稱") #JSON文件名稱,必須是json格式,由於內部會執行json.loads app.config.from_mapping({'DEBUG':True}) ###字典格式 app.config.from_object("python類或類的路徑」) ###推薦 ###示例: app.config.from_object('pro_flask.settings.TestingConfig') settings.py class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config): DATABASE_URI = 'mysql://user@localhost/foo' class DevelopmentConfig(Config): DEBUG = True class TestingConfig(Config): TESTING = True #PS: 從sys.path中已經存在路徑開始寫 #更多配置參數參考:http://flask.pocoo.org/docs/1.0/config/#builtin-configuration-values
flask的路由系統可能區別與django之一在於其路由的實現是由裝飾器完成的。sql
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # Author:wd from flask import Flask app=Flask(__name__) #####方式一,裝飾器方式,endpoint至關於別名,用於反向生成url @app.route(rule='/index',endpoint='a1') def index(): return 'index' def index1(): return 'index1' ####方式二,使用函數方式 app.add_url_rule(rule='/index1',endpoint='a2',view_func=index1)
經常使用參數數據庫
經過把 URL 的一部分標記爲 <variable_name> 就能夠在 URL 中添加變量。標記的 部分會做爲關鍵字參數傳遞給函數。經過使用 <converter:variable_name> ,能夠 選擇性的加上一個轉換器,爲變量指定規則。django
@app.route('/user/<username>') # 普通規則,username至關於變量,在url中指定例如/user/wd,username就是wd def show_user_profile(username): return 'User %s' % username @app.route('/post/<int:post_id>') # 整型轉換器,post後面只能是int類型,不然404 def show_post(post_id): return 'Post %d' % post_id @app.route('/path/<path:subpath>') # 路徑轉換器 def show_subpath(subpath): return 'Subpath %s' % subpath
內置轉換器json
DEFAULT_CONVERTERS = { 'default': UnicodeConverter, #默認數據類型,但不包含斜槓的文本 'string': UnicodeConverter, #默認數據類型 'any': AnyConverter, #多個路徑 'path': PathConverter, #相似字符串,能夠包含斜槓 'int': IntegerConverter, #只能是int類型 'float': FloatConverter, #只能是float類型 'uuid': UUIDConverter, #只能是uuid字符串 }
在django中咱們能夠經過reverse方法反向生成url,一樣在flask也能夠經過url_for反向生成。flask
# -*- coding:utf-8 -*- # Author:wd from flask import Flask,url_for app = Flask(__name__) @app.route('/user/<username>',endpoint='a1') def show_user_profile(username): print(url_for('a1',username='jack', next='/')) #可根據須要傳遞參數,第一個參數是視圖函數或者endpoint,第二個參數是須要傳遞的參數,next根據需求 print(url_for('a1', username='jack')) return 'User %s' % username if __name__ == '__main__': app.run() #結果 #/user/jack?next=%2F #/user/jack
擴展本身的自定義URL規則須要繼承BaseConverter,重寫to_url方法。
from flask import Flask,url_for app = Flask(__name__) # 定義轉換的類 from werkzeug.routing import BaseConverter class RegexConverter(BaseConverter): """ 自定義URL匹配正則表達式 """ def __init__(self, map, regex): super(RegexConverter, self).__init__(map) self.regex = regex def to_python(self, value): """ 路由匹配時,匹配成功後傳遞給視圖函數中參數的值 :param value: :return: """ return int(value) def to_url(self, value): """ 使用url_for反向生成URL時,傳遞的參數通過該方法處理,返回的值用於生成URL中的參數 :param value: :return: """ val = super(RegexConverter, self).to_url(value) return val # 添加到converts中 app.url_map.converters['regex'] = RegexConverter # 進行使用 @app.route('/index/<regex("\d+"):nid>',endpoint='xx') def index(nid): url_for('xx',nid=123) #反向生成,就會去執行to_url方法 return "Index" if __name__ == '__main__': app.run()
在django中視圖分爲CBV和FBV,固然Flask視圖也分CBV和FBV
FBV
###方式一: @app.route('/index',endpoint=‘a1') def index(nid): return "Index" ###方式二: def index(nid): return "Index" app.add_url_rule('/index',index)
CBV
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # Author:wd from flask import url_for, views, Flask app = Flask(__name__) def auth(func): # 裝飾器 def inner(*args, **kwargs): print('require auth') result = func(*args, **kwargs) return result return inner class IndexView(views.MethodView): methods = ['GET', 'POST'] # 只容許GET、POST請求訪問 decorators = [auth, ] # 若是想給全部的get,post請求加裝飾器,就能夠這樣來寫,也能夠單個指定 def get(self): # 若是是get請求須要執行的代碼 v = url_for('index') print(v) return "GET" def post(self): # 若是是post請求執行的代碼 return "POST" app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name指定的是別名,會當作endpoint使用 if __name__ == '__main__': app.run()
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # Author:wd from flask import url_for, views, Flask app = Flask(__name__) def auth(func): # 裝飾器 def inner(*args, **kwargs): print('require auth') result = func(*args, **kwargs) return result return inner ####FBV裝飾器 @app.route(‘/login', methods=['GET', 'POST']) @auth # 注意若是要給視圖函數加裝飾器,一點要加在路由裝飾器下面,纔會被路由裝飾器裝飾 def login(): return 'login' class IndexView(views.MethodView): methods = ['GET', 'POST'] # 只容許GET、POST請求訪問 ####CBV裝飾器 decorators = [auth, ] # 若是想給全部的get,post請求加裝飾器,就能夠這樣來寫,也能夠單個指定 def get(self): # 若是是get請求須要執行的代碼 v = url_for('index') print(v) return "GET" def post(self): # 若是是post請求執行的代碼 return "POST" app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name指定的是別名,會當作endpoint使用 if __name__ == '__main__': app.run()
在django中經過request獲取請求信息經過render、httpresponse等響應數據,一樣在flask中也是經過request來獲取請求數據,requset須要導入。
#導入 from flask import request ####請求相關信息 request.method:獲取請求方法 request.json.get("json_key"):獲取json數據 **較經常使用 request.argsget('name') :獲取get請求參數 request.form.get('name') :獲取POST表單請求參數 request.form.getlist('name_list'):獲取POST表單請求參數列表(如多選) request.values.get('age') :獲取GET和POST請求攜帶的全部參數(GET/POST通用) request.cookies.get('name'):獲取cookies信息 request.headers.get('Host'):獲取請求頭相關信息 request.path:獲取用戶訪問的url地址,例如(/,/login/,/ index/); request.full_path:獲取用戶訪問的完整url地址+參數 例如(/login/?name=wd) request.url:獲取訪問url地址,例如http://127.0.0.1:5000/?name=18 request.base_url:獲取訪問url地址,例如 http://127.0.0.1:5000/; request.url_root:不帶參數的根url,例如 http://127.0.0.1:5000/; request.host_url:不帶參數的根host,當使用ip方式訪問的時候同url_root例如 http://127.0.0.1:5000/; request.host:獲取主機地址 request.files:獲取用戶上傳的文件 obj = request.files['the_file_name'] obj.save('/var/www/uploads/' + secure_filename(f.filename)) 直接保存文件
################基本響應類型 return "字符串" :響應字符串 return render_template('html模板路徑',**{}):響應模板 return redirect('/index.html'):跳轉頁面 ##############JSON格式響應 #!/usr/bin/env python3 # -*- coding:utf-8 -*- # Author:wd from flask import url_for, views, Flask, request, Response import json app = Flask(__name__) @app.route('/login', methods=['GET', 'POST']) def login(): response = Response(json.dumps({"status":"ok"}), mimetype="application/json;charset=utf-8") return response if __name__ == '__main__': app.run() ##################設置響應信息 #導入 from flask import make_response response = make_response(render_template('index.html’)) #也能夠相應模版 response.delete_cookie('key’) #刪除cookie中的一個key response.set_cookie('key', 'value’) #設置cookie response.headers['username'] = ‘wd’ #設置響應頭信息 return response
flask的模版引擎和django同樣都採用Jinja2(參考:http://jinja.pocoo.org/docs/2.10/templates/)
{{ key }}
{% for item in item_list %} <a>{{ item }}</a> {% endfor %}
demo:
<html> <body> <!-- 列表循環 --> {% for i in k1 % } <h1>{{ i }}</h1> {% endfor % } <!-- 字典循環 --> {% for k in k2.keys % } <h1>{{ k }}</h1> {% endfor % } {% for v in k2.values % } <h1>{{ v }}</h1> {% endfor % } {% for k,v in k2.items % } <h1>{{ k }}-{{ v }}</h1> {% endfor % } </body> </html>
{% if ordered_warranty %} {% else %} {% endif %}
母模版: {% block name %} {% endblock %} 使用: 先聲明: {% extends 'html模版' %} {% block name %} {% endblock %}
當某個元素屢次使用時候,咱們能夠定義宏
{% macro input(name, type='text', value='') %} <input type="{{ type }}" name="{{ name }}" value="{{ value }}"> {% endmacro %} {{ input('n1') }}
若是防止HTML顯示爲字符串,flask採用Markup實現,而django採用make_safe。
###後端 return Markup("<input type='text' value='wd'>") ###前端 {{"<input type='text'>"|safe}}
Flask的Jinjia2能夠經過Context 把視圖中的函數傳遞把模板語言中執行,這就是Django中的simple_tag和simple_fifter功能。
simple_tag(只能傳2個參數,支持for、if)
#!/usr/bin/env python3 #_*_ coding:utf-8 _*_ #Author:wd from flask import Flask,render_template app = Flask(__name__) @app.route('/index') def index(): return render_template('index.html') @app.template_global() #simple_tag def foo(arg): return "<input type='text' value='{}'>".format(arg) if __name__ == '__main__': app.run()
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div>{{foo('wd')|safe }}</div> </body> </html>
simple_fifter(對參數個數無限制,不支持for、if)
#!/usr/bin/env python3 #_*_ coding:utf-8 _*_ #Author:wd from flask import Flask,render_template,Markup app = Flask(__name__) @app.route('/login') def login(): return render_template('login.html') @app.template_filter() #simple_fifter def bar(arg1,arg2,arg3): return Markup("<input type='text' value='{}'>".format(arg1+arg2+arg3)) if __name__ == '__main__': app.run()
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div>{{ 'wd'|bar('name','is ')}}</div> </body> </html>
除請求對象以外,還有一個 session 對象,本質上來講就是一個字典。它容許你在不一樣請求間存儲特定用戶的信息。它是在 Cookies 的基礎上實現的,而且對 Cookies 進行密鑰簽名要使用會話,須要設置一個密鑰。
#設置session:session[‘username'] = ‘wd’ #刪除session:del session[‘username’]或者session.pop(‘username’) #清空session:session.clear()
簡單的登陸驗證demo:
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # Author:wd from flask import session, Flask, request, redirect, render_template app = Flask(__name__) app.secret_key = 'adsadq2dq' # 設置加密的鹽 app.config['SESSION_COOKIE_NAME'] = 'session_wd' # 設置session的名字 def auth(func): # 登陸認證裝飾器 def inner(*args, **kwargs): if not session.get('username'): return redirect('/login') return func(*args, **kwargs) return inner @app.route('/index', methods=['GET', 'POST']) @auth def index(): return render_template('index.html') @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == "GET": return render_template('login.html') if request.form.get('username') == 'wd' and request.form.get('password') == '123': session['username'] = 'wd' return redirect('/index') @app.route('/logout') def logout(): session.clear() return redirect('/login') if __name__ == '__main__': app.run()
配置方法在以前已經提到,如 app.config['SESSION_COOKIE_NAME'] = 'session_wd'
'SESSION_COOKIE_NAME': 'session’, # session 名稱配置 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_REFRESH_EACH_REQUEST': True, #是否每次都跟新 'PERMANENT_SESSION_LIFETIME': timedelta(days=31) #設置seesion超時時間
正如上面介紹,flask簡便易用,一個py文件就能夠完成一個小項目,當項目相對大時候,咱們就須要將目錄進行結構劃分,藍圖的功能就在於此。
目錄結果
monitor #項目主目錄 ├── __init__.py ├── runserver.py #啓動腳本 ├── statics #靜態文件 ├── templates #模版目錄 └── views #視圖目錄 ├── account.py └── permission.py
各個py文件:
from flask import Flask from .views import account from .views import permission app = Flask(__name__, template_folder='templates', static_folder='statics', static_url_path='/static') app.register_blueprint(blueprint=account.login_bp,url_prefix='/account’) #註冊一個藍圖 app.register_blueprint(blueprint=permission.permit_bp, url_prefix='/permit')
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # Author:wd from flask import Blueprint login_bp=Blueprint('login',__name__) #實例化一個藍圖 @login_bp.route('/login',methods=['GET','POST']) def login(): return '登陸頁'
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # Author:wd from monitor import app if __name__=='__main__': app.run()
Flask 的閃現系統提供了一個良好的反饋方式。閃現系統的基 本工做方式是:在且只在下一個請求中訪問上一個請求結束時記錄的消息。通常咱們結合佈局模板來使用閃現系統。消息閃現原理是flask的 session組件而該組件是基於cookie的,瀏覽器會限制 cookie 的大小,有時候網絡服 務器也會。這樣若是消息比會話 cookie 大的話,那麼會致使消息閃現靜默失敗。
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # Author:wd from flask import Flask, flash, get_flashed_messages, request, render_template, redirect, url_for app = Flask(__name__) app.secret_key = '123addqe1' @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == "POST": if request.form.get('username') == 'wd' and request.form.get('password') == '123': flash(message='wd') # 設置消息 return redirect(url_for('index')) else: flash(message='用戶名或者密碼錯誤') return render_template('login.html') @app.route('/index', methods=['GET', 'POST'], endpoint='index') def index(): messge = get_flashed_messages() # 獲取消息 return 'ok username {}'.format(''.join(messge)) if __name__ == "__main__": app.run()
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="post"> <div><input type="text" name="username"></div> <div><input type="password" name="password"></div> <div><input type="submit" name="password"></div> <div> {% with messages = get_flashed_messages() %} {% if messages %} <ul class=flashes> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} </div> </form> </body> </html>
閃現消息還能夠指定類別,若是沒有指定,那麼缺省的類別爲 'message' 。不一樣的類別能夠給用戶提供更好的反饋,獲取改級別的時候須要加參數with_categories=True。例如錯誤消息能夠error,此時的消息是一個tuple。
示例:
@app.route('/login', methods=['GET', 'POST']) def login(): if request.method == "POST": if request.form.get('username') == 'wd' and request.form.get('password') == '123': flash(message='wd') # 設置消息 return redirect(url_for('index')) else: flash(message='用戶名或者密碼錯誤',category='ERROR') print(get_flashed_messages(with_categories=True)) # 結果[('ERROR', '用戶名或者密碼錯誤')] return render_template('login.html')
若是想獲得某個指定類別或者多個類別的消息則在獲取的時候使用參數category_filter
messge = get_flashed_messages(category_filter=["ERROR",]) # 獲取指定類型的消息
flask的請求鉤子是經過裝飾器實現的,經過這些鉤子函數咱們能夠在請求的每一個階段執行本身的業務邏輯。
如下是經常使用請求擴展裝飾器:
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # Author:wd from flask import Flask, Request, render_template app = Flask(__name__, template_folder='templates') @app.before_first_request # 第1個請求到來執行 def before_first_request1(): print('before_first_request1') @app.before_request # 中間件2 def before_request1(): Request.nnn = 123 print('before_request1') # 不能有返回值,一旦有返回值在當前返回 @app.before_request def before_request2(): print('before_request2') @app.errorhandler(404) # 404錯誤處理 def page_not_found(error): return 'This page does not exist', 404 @app.route('/') def index(): return "Index" @app.after_request # 中間件 執行視圖以後 def after_request1(response): print('after_request1', response) return response @app.after_request # 中間件 執行視圖以後 先執行 after_request2 def after_request2(response): print('after_request2', response) return response if __name__ == '__main__': app.run() #結果:
#before_request1 #before_request2 #after_request2 <Response 5 bytes [200 OK]> #after_request1 <Response 5 bytes [200 OK]>