Flask 是一個微型 Web 框架,依賴於 jinjia2 模板系統和 Werkzeug WSGI(本質爲 Socket 服務端) 服務,默認狀況不支持數據庫抽象層、表單驗證,若是要使用能夠進行拓展。css
Flask 經常使用擴展包:html
安裝:前端
pip3 install flask
Werkzeug:python
#!/usr/bin/env python # -*- coding:utf-8 -*- from werkzeug.wrappers import Request, Response @Request.application def hello(request): return Response('Hello World!') if __name__ == '__main__': from werkzeug.serving import run_simple run_simple('localhost', 4000, hello)
嚐鮮:shell
from flask import Flask app = Flask(__name__) @app.route("/") def index(): return "Index Page" if __name__ == "__main__": app.run()
官網地址:數據庫
Flask 經常使用五種路由:flask
# 前四種能夠接收變量 @app.route('/user/<username>') # /user/rose @app.route('/post/<int:post_id>') # /post/1 @app.route('/post/<float:post_id>') # /post/1.2 @app.route('/post/<path:subpath>') # /post/static/css/xx.css @app.route('/login', methods=['GET', 'POST'])
示例:bootstrap
@app.route('/user/<username>') def show_user_profile(username): return 'User %s' % username
以上五種路由基於如下對應關係:segmentfault
DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
讓 Flask 支持正則:https://segmentfault.com/q/1010000000125259
。api
url_for()
方法能夠根據視圖函數名,反解出 url,相似於 Django 的 reverse()
方法:
# 接收視圖函數名字,返回所對應的絕對 URL url_for('index', _external=True)
from flask import Flask, redirect, url_for app = Flask(__name__) @app.route("/") def index(): return "Index Page" @app.route("/hello") def hello(): return "Hello World!" @app.route('/user/<username>') def profile(username): return username with app.test_request_context(): print(url_for('hello')) print(url_for('profile', username='rose')) print(url_for('index')) if __name__ == "__main__": app.run()
test_request_context()
告訴 Flask
,即便咱們使用Python shell
,它也會像處理請求同樣行事。
運行結果以下:
/hello /user/rose /
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 if __name__ == '__main__': app.run()
from flask import Flask, redirect, url_for, render_template app = Flask(__name__) # 靜態文件和模板依賴這句 @app.route('/hello/') def hello(): return render_template('t1.html') if __name__ == '__main__': app.run()
靜態文件語法:
url_for('static', filename='style.css')
靜態文件目錄 static
在項目根目錄,與程序同級(須要本身建立),Flask 默認回會去 static
中查找,你能夠在裏面新建子文件夾:
<img src="{{ url_for('static', filename='imgs/1.png') }}">
模板路徑設置
Flask 會在 templates
文件夾裏尋找模板。因此,若是你的應用是個模塊,這個文件夾應該與模塊同級;若是它是一個包,那麼這個文件夾做爲包的子目錄:
狀況 1: 模塊:
/application.py /templates /hello.html
狀況 2: 包:
/application /__init__.py /templates /hello.html
Flask 使用的是 Jinja2模板,與 Django 同樣,所以語法也沒什麼差異。
自定義模板
在模板中使用 Python 函數:
app.py
#!/usr/bin/env python # -*- coding:utf-8 -*- from flask import Flask, render_template app = Flask(__name__) def func(): return '<h1>Flask 自定義模板</h1>' @app.route('/index', methods=['GET', 'POST']) def index(): return render_template('index.html', func=func) if __name__ == '__main__': app.run()
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ func() | safe }} <!-- safe 不轉義 --> </body> </html>
語法:
變量名 | 過濾器
字符串過濾器:
# 禁用轉義 safe <p>{{ '<em>hello</em>' | safe }}</p> # capitalize:把變量值的首字母轉成大寫,其他字母轉小寫; <p>{{ 'hello' | capitalize }}</p> # lower:把值轉成小寫; <p>{{ 'HELLO' | lower }}</p> # upper:把值轉成大寫; <p>{{ 'hello' | upper }}</p> # title:把值中的每一個單詞的首字母都轉成大寫; <p>{{ 'hello' | title }}</p> #trim:把值的首尾空格去掉; <p>{{ ' hello world ' | trim }}</p> # reverse:字符串反轉; <p>{{ 'olleh' | reverse }}</p> # format:格式化輸出; <p>{{ '%s is %d' | format('name',17) }}</p> # striptags:渲染以前把值中全部的HTML標籤都刪掉; <p>{{ '<em>hello</em>' | striptags }}</p>
列表操做:
# first:取第一個元素 <p>{{ [1,2,3,4,5,6] | first }}</p> # last:取最後一個元素 <p>{{ [1,2,3,4,5,6] | last }}</p> # length:獲取列表長度 <p>{{ [1,2,3,4,5,6] | length }}</p> # sum:列表求和 <p>{{ [1,2,3,4,5,6] | sum }}</p> # sort:列表排序 <p>{{ [6,2,3,1,5,4] | sort }}</p>
自定義過濾器
過濾器的本質是函數,兩種實現方式:一種是經過Flask應用對象的add_template_filter
方法。還能夠經過裝飾器來實現自定義過濾器,與內置過濾器重名則覆蓋:
一、add_template_filter
,第一個參數是函數名,第二個參數是自定義的過濾器名稱
def filter_double_sort(ls): return ls[::2] app.add_template_filter(filter_double_sort,'double_2')
二、裝飾器來實現自定義過濾器,參數爲過濾器名字:
@app.template_filter('db3') def filter_double_sort(ls): return ls[::-3]
app.py
from flask import Flask, render_template app = Flask(__name__) def filter_double_sort(ls): return ls[::2] app.add_template_filter(filter_double_sort, 'double_2') @app.route('/') def index(): data_list = [1, 2, 5, 8] return render_template('index4.html', data_list=data_list) if __name__ == "__main__": app.run()
index4.html
<p>{{ data_list | double_2}}</p>
相似於 python
中的函數,宏的做用就是在模板中重複利用代碼,避免代碼冗餘。
定義不帶參數的宏:
<!-- 定義宏 --> {% macro input() %} <input type="text" name="username" value="" size="30"/> {% endmacro %} <!-- 調用宏 --> {{ input() }}
定義帶參數的宏:
{% macro input(name,value='',type='text',size=20) %} <input type="{{ type }}" name="{{ name }}" value="{{ value }}" size="{{ size }}"/> {% endmacro %} <!-- 調用宏,並傳遞參數 --> {{ input(value='name',type='password',size=40)}}
還能夠把宏單獨抽取出來,封裝成 html
文件,其它模板中導入使用文件名能夠自定義macro.html
:
{% macro function() %} <input type="text" name="username" placeholde="Username"> <input type="password" name="password" placeholde="Password"> <input type="submit"> {% endmacro %}
在其它模板文件中先導入,再調用:
{% import 'macro.html' as func %} {% func.function() %}
宏、繼承(extend)和包含(include)區別:
{{ config.SQLALCHEMY_DATABASE_URI }}
get_flashed_messages
方法:應用在模板中通常用於獲取錯誤信息,返回以前在Flask中經過 flash() 傳入的信息列表。把字符串對象表示的消息加入到一個消息隊列中,而後經過調用 get_flashed_messages() 方法取出。{% for message in get_flashed_messages() %} {{ message }} {% endfor %}
一、請求 request
瀏覽器發送過來的 HTTP 請求,被 flask 封裝在 request中(werkzeug.wrappers.BaseRequest)
中,提供了一下字段或方法以供使用:
request.method # 請求方法 request.args # 獲取GET請求參數 request.form # 獲取POST請求參數 request.values request.files # 獲取上傳文件 request.cookies # cookies request.headers # 請求頭 request.path request.full_path request.script_root request.url # 請求 URL request.base_url # 獲取請求路徑 request.url_root request.host_url request.host # 獲取ip和端口
二、響應 response
一、模板渲染、重定向
模板渲染:render_template
、重定向:redirect
、url_for
from flask import Flask, render_template, request, redirect, url_for app = Flask(__name__) @app.route('/index/', method=['GET', 'POST']) def index(): return render_template('index.html') # redirect(url_for('login')) # 重定向
二、錯誤信息
from flask import Flask, abort, render_template app = Flask(__name__) @app.route('/index/', methods=['GET', 'POST']) def index(): abort(404, 'Page Not Found!') # 返回一個 Page Not Found! 的頁面 if __name__ == '__main__': app.run()
三、make_reponse
make_response
相似於 Django 的 HTTPResponse
,用於返回字符串,下面是幾個比較常見的用法:
返回內容、頁面:
from flask import Flask, abort, render_template, make_response app = Flask(__name__) def func(): return '<h1>Flask 自定義模板</h1>' @app.route('/index/', methods=['GET', 'POST']) def index(): # return make_response('make response') # 返回內容 response = make_response(render_template('index.html', func=func)) # 返回頁面 # return response, 200, # 返回內容和狀態碼
response
是 flask.wrappers.Response
類型,同時 make_resposne
還能夠用來設置響應頭、設置 cookie 等:
# response.delete_cookie # response.set_cookie # response.headers['X-Something'] = 'A value'
@app.route('/index/', methods=['GET', 'POST']) def index(): response = make_response(render_template('index.html', func=func)) response.headers['hahaha'] = 'hello world' return response
若是在瀏覽器中輸入不可用的路由,那麼就會顯示一個 404 狀態的錯誤頁面,這個頁面太過簡陋、平庸,並非咱們所但願的。好在 Flask 容許咱們基於模板自定義錯誤頁面,須要用到模塊:flask-bootstrap
。
安裝:
pip3 install flask-bootstrap
下面來示範兩個最多見的錯誤:404 和 500:
一、app.py
初始化 bootstrap
,定義兩個函數,用於處理 404 和 500 錯誤頁面:
from flask import Flask, abort, render_template from flask_bootstrap import Bootstrap app = Flask(__name__) bootstrap = Bootstrap(app) # 初始化,千萬別忘記 @app.route('/index/', methods=['GET', 'POST']) def index(): return "OK" @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()
二、在模板中定義一個母版 templates/errors/base.html
,編輯以下:
{%extends "bootstrap/base.html"%} {%block title %}Flask{% 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"> {% block page_content %}{% endblock %} </div> </div> {% endblock %}
三、下面再編寫 404.html
和 500.html
404.html
{% extends "base.html" %} {% block title %}Flasky - Page Not Found{% endblock %} {% block page_content %} <div class="page-header"> <h1>Not Found</h1> </div> {% endblock %}
500.html
{% extends "base.html" %} {% block title %}Flasky - internal server error{% endblock %} {% block page_content %} <div class="page-header"> <h1>internal server error</h1> </div> {% endblock %}
如今咱們來訪問一個不存在的頁面看看:http://127.0.0.1:5000/main/
彷佛好看了點,固然你還能夠定製更爲高端一點。
Session 依賴於 Cookies ,而且對 Cookies 進行密鑰簽名要使用會話,你須要設置一個密鑰。
session['username'] = 'xxx'
session.pop('username', None)
session.get('username')
設置獲取 Cookie
from flask import Flask,make_response @app.route('/cookie') def set_cookie(): resp = make_response('this is to set cookie') resp.set_cookie('username', 'itcast') return resp from flask import Flask,request #獲取cookie @app.route('/request') def resp_cookie(): resp = request.cookies.get('username') return resp
示例:利用 Session 驗證用戶登陸
from flask import Flask, session, redirect, url_for, escape, request app = Flask(__name__) @app.route('/') def index(): if 'username' in session: return '歡迎回來: %s' % escape(session['username']) return '你沒有登陸' # 也能夠重定向到登陸頁面 @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': session['username'] = request.form['username'] return redirect(url_for('index')) return ''' <form action="" method="post"> <p><input type=text name=username> <p><input type=submit value=Login> </form> ''' @app.route('/logout') def logout(): # 從 session 移除 username session.pop('username', None) return redirect(url_for('index')) # 設置密鑰 app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' if __name__ == '__main__': app.run()
message 是一個基於 Session 實現的用於保存數據的集合,其特色是:使用一次就刪除
app.py
from flask import Flask, flash, redirect, render_template, request app = Flask(__name__) app.secret_key = 'some_secret' @app.route('/') def index1(): return render_template('index2.html') @app.route('/set') def index2(): v = request.args.get('p') flash(v) # 將消息給下一個請求,並將其刪除,模板必須調用 get_flashed_messages() 函數 return 'ok' if __name__ == "__main__": app.run()
index2.html
模板中必須使用 get_flashed_messages()
才能獲取到 message
傳遞的信息:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>messages</title> </head> <body> {% with messages = get_flashed_messages() %} <!-- with xxx:取別名 --> {% if messages %} <ul class=flashes> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} </body> </html>
首先請求:http://127.0.0.1:5000/set?p=123
,index()
函數獲取 p 的值,並將其傳遞給下一個請求(傳遞後即將其刪除)。
第二個請求訪問:http://127.0.0.1:5000/
,從而得到上一次請求傳遞的信息:
所謂請求鉤子便是用來處理請求前、後,一些特定事情的功能實現。如:請求前鏈接數據庫、請求後指定數據交互格式等。Flask 中請求鉤子以裝飾器形式實現:
Flask 中請求鉤子:
示例:基於 before_request
實現用戶登陸認證
相比較裝飾器而已有以下優點:
before_request
,只需一個便可。from flask import Flask, render_template, request, redirect, session, url_for app = Flask(__name__) app.secret_key = "123$456" # 基於flask裏請求擴展來作 @app.before_request def process_request(*args, **kwargs): """無論訪問哪一個視圖函數,都會實現執行這個函數,也就意味着每次都會檢查是否登陸""" # 訪問/login的時候尚未登陸,就會一直重定向到登陸頁,因此就要設置個白名單,若是請求地址是/login,就返回None if request.path == "/login": return None # 1.登陸驗證功能 user = session.get('user_info') # 2.若是登陸信息正常,什麼都不作,程序繼續其餘執行 if user: return None # 3.若是登陸驗證不經過,就重定向到登陸頁面 return redirect("/login") @app.route("/index", methods=['GET']) def index(): name = session.get('user_info', None) return 'Welcome, %s' % name @app.route("/login", methods=['GET', 'POST']) def login(): if request.method == "GET": return render_template("login.html") else: user = request.form.get("username") pwd = request.form.get("password") if user == "rose" and pwd == "123456": session['user_info'] = user return redirect("/index") return render_template("login.html", **{"error": "用戶名和密碼錯誤"}) if __name__ == "__main__": app.run()
也能夠有多個請求鉤子,可是要注意的是當有多個請求中間件時,執行順序是不一樣的:
before_request
:從上到下按照順序執行after_request
:從下到上的順序執行before_request
時,若第一個有 return
語句,那麼後面的 before_request
將被攔截不被執行,可是 after_request
會繼續執行Flask 程序在運行過程當中會事先加載 wsgi_app
,基於此咱們能夠拓展作些別的事情:
from flask import Flask app = Flask(__name__) @app.route("/login", methods=['GET', 'POST']) def index(): pass class Md(object): def __init__(self, old_wsgi_app): self.old_wsgi_app = old_wsgi_app def __call__(self, environ, start_response): print("開始以前") ret = self.old_wsgi_app(environ, start_response) print("結束以後") return ret if __name__ =="__main__": app.wsgi_app = Md(app.wsgi_app) # 至關於把wsgi_app給更新了 app.run()
表單由三個部分組成:表單標籤、表單域、表單按鈕。Flask 經過Flask-WTF 實現表單功能。它封裝了WTForms,能夠生成表單、以及驗證表單數據。
安裝:
pip3 install Flask-WTF pip3 install flask-moment
WTForms 支持的 HTML 標準字段:
StringField # 文本字段 TextAreaField # 多行文本字段 PasswordField # 密碼文本字段 HiddenField # 隱藏文本字段 DateField # 文本字段,值爲datetime.date格式 DateTimeField # 文本字段,值爲datetime.datetime格式 IntegerField # 文本字段,值爲整數 DecimalField # 文本字段,值爲decimal.Decimal FloatField # 文本字段,值爲浮點數 BooleanField # 複選框,值爲True和False RadioField # 一組單選框 SelectField # 下拉列表 SelectMultipleField # 下拉列表,可選擇多個值 FileField # 文本上傳字段 SubmitField # 表單提交按鈕 FormField # 把表單做爲字段嵌入另外一個表單 FieldList # 一組指定類型的字段
WTForms 經常使用驗證函數:
驗證函數 | 說明 |
---|---|
DataRequired | 確保字段中有數據 |
AEqualTo | 比較兩個字段的值,經常使用於比較兩次密碼輸入 |
Length | 驗證輸入的字符串長度 |
NumberRange | 驗證輸入的值在數字範圍內 |
URL | 驗證URL |
AnyOf | 驗證輸入值在可選列表中 |
NoneOf | 驗證輸入值不在可選列表中 |
跨站請求僞造保護 csrf
設置一個密匙,生成加密令牌,再用令牌驗證請求表單中數據真僞:
app = Flask(__name__) app.config['SECRET_KEY'] = 'hard to guess string' # 密匙隨便取 # 若是你設置的模板中存在表單,你只須要在表單中添加以下 <form method="post" action="/"> {{ form.csrf_token }} </form> # 若是沒有模板中沒有表單,你仍然須要一個 CSRF 令牌 <form method="post" action="/"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> </form>
簡單示例
一、定義一個表單類:
from flask_wtf import FlaskForm #導入自定義表單須要的字段 from wtforms import SubmitField, StringField, PasswordField #導入wtf擴展提供的表單驗證器 from wtforms.validators import DataRequired, EqualTo # validators 不能爲空 class NameForm(FlaskForm): name = StringField(label='用戶名', validators=[Required()]) submit = SubmitField('提交')
二、在模板中使用表單:
<h1>Hello, {% if name %}{{ name }} {% else %} Stranger {% endif %}!</h1> <form method='post'> {{ form.hidden_tag() }} {{ form.name.label }} {{form.name() }} {{ form.submit() }} # 傳入 HTML 屬性 {{ form.name.label }} {{form.name(id='id_name') }} </form>
老是本身傳屬性太麻煩,並且表單樣式過於醜陋。能夠用 flask-bootstrap
,它是對 bootstrap
進行了簡單封裝,安裝好以後能夠直接調用:
<!--上面表單可用下面方式一次渲染--> {% import 'bootstrap/wtf.html' as wtf %} {{ wtf.quick_form(form) }}
from flask_bootstrap import Bootstrap app = Flask(__name__) bootstrap = Bootstrap(app)
wtf.quick_form(form)
函數參數爲 Flask-WTF
表單對象
<!-- 條件結果爲 True,渲染 if else 中文字,不然渲染 else endif 中語句 --> <h1>Hello, {% if name %}{{ name }} {% else %} Stranger {% endif %}!</h1>
實現方式:繼承+WTForms +Flask-Bootstrap
app.py
from flask import Flask, render_template, redirect, url_for, session, request,flash from flask_bootstrap import Bootstrap from flask_moment import Moment #導入wtf擴展的表單類 from flask_wtf import FlaskForm #導入自定義表單須要的字段 from wtforms import SubmitField, StringField, PasswordField #導入wtf擴展提供的表單驗證器 from wtforms.validators import DataRequired, EqualTo app = Flask(__name__) app.config['SECRET_KEY'] = '1' bootstrap = Bootstrap(app) # 使用 bootstrap 渲染表單 moment = Moment(app) # 本地化時間 #自定義表單類,文本字段、密碼字段、提交按鈕 class Login(FlaskForm): user = StringField(label=u'用戶:', validators=[DataRequired()]) pwd1 = PasswordField(label=u'密碼', validators=[DataRequired()]) pwd2 = PasswordField(label=u'確認密碼', validators=[DataRequired()]) submit = SubmitField(u'提交') @app.route('/', methods=['GET','POST']) def index(): return render_template('index.html') #定義根路由視圖函數,生成表單對象,獲取表單數據,進行表單數據驗證 @app.route('/login', methods=['GET','POST']) def login(): form = Login() if form.validate_on_submit(): name = form.user.data pwd1 = form.pwd1.data pwd2 = form.pwd2.data print(name, pwd1, pwd2) if name == 'rose' and (pwd1 == pwd2 and pwd2 == '123'): return redirect(url_for('index')) else: flash(u'用戶名或密碼錯誤!') return render_template('login.html', form=form) if __name__ == '__main__': app.run()
include/base.html
{% extends "bootstrap/base.html" %} {% block title %}Flask WTF{% endblock %} {% 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 %} {% 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="/">Flask WTF</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"> <!-- get_flashed_messages() 顯示錯誤信息 --> {% for message in get_flashed_messages() %} <div class="alert alert-warning"> <button type="button" class="close" data-dismiss="alert">×</button> {{ message }} </div> {% endfor %} {% block page_content %}{% endblock %} </div> {% endblock %} {% block scripts %} {{ super() }} {{ moment.include_moment() }} {% endblock %}
login.html
繼承 base.html
{% extends "include/base.html" %} {% import "bootstrap/wtf.html" as wtf %} {% block title %}Flask WTF{% endblock %} {% block page_content %} <div class="page-header"> </div> {{ wtf.quick_form(form) }} {% endblock %}
將用戶我的信息保存到會話中:
app.py
from flask import Flask, render_template, redirect, url_for, session, request,flash def login('/login', methods=['GET', 'POST']): ... # 會話中原有值 old_name = session.get('name') if old_name is not None and old_name != form.name.data: flash('用戶名錯誤!') session['name'] = form.name.data return redirect(url_for('index')) return render_template('login.html', form=form, name=session.get('name'))
login.html
<div class="page-header"> <h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1> </div>