repo: github.com/alphardex/p…javascript
flask是一個Python語言開發的web「微框架」,和django不一樣的是,它既沒有數據庫、也沒有表單驗證等工具它自己僅僅提供了一個WSGI的橋樑,其餘東西通通靠你來定製,具備很大的靈活性css
爲了迅速搭建一個像樣的flask網站,咱們可使用腳手架html
以前在Github上看到cookiecutter-flask,是個不錯的選擇,可是新手可能會看不懂裏面代碼是如何封裝的java
因而本人作出了一個更user-friendly的腳手架——cookiecutter-flask-bootstrappython
這個腳手架的功能大體和上個腳手架差很少,不過更加輕量化,並且結構更加清晰明瞭,best practice也基本都作到了,但願你們用的開心:dgit
最後還要感謝李輝大大的狼書,給了我很大的幫助github
路由是將特定的業務代碼(即視圖函數)綁定到某個url上,以實現某個功能web
flask中使用裝飾器來註冊路由sql
@app.route('/')
def index():
return 'Hello world.'
複製代碼
能夠爲路由傳遞變量,變量左邊能夠帶轉換器用來轉換變量的類型mongodb
@app.route('/user/<string:username>')
def user_profile(username):
return f'User {username}'
複製代碼
經常使用的轉換器有6種:string, int, float, path, any, uuid
比較特殊的是any,格式以下(var變量只接受a, b其中的任意一值)
@app.route('/<any(a, b):var>/')
複製代碼
若是想經過路由直接訪問文件呢?用path轉換器和send_from_directory就好了
@app.route('/uploads/<path:filename>')
def get_image(filename):
return send_from_directory(current_app.config['UPLOAD_PATH'], filename)
複製代碼
使用url_for函數能夠反向構建訪問路由的url
url_for('index') # '/'
url_for('user_profile', username='alphardex') # '/user/alphardex'
url_for('index', _external=True) # 'http://localhost:5000/' 絕對路徑
複製代碼
路由默認支持GET方法,若是須要POST方法則需在route的methods中傳入
HTTP methods的用處以下:
@app.route('/login', methods=['GET', 'POST'])
複製代碼
若是想更改HTTP請求頭內容,那就要用到make_response
好比製做網站的RSS,就須要把響應的mimetype設置爲application/xml
@app.route('/rss')
def rss():
articles = Article.query.order_by(Article.date.desc).limit(10)
rss = render_template('rss.xml', articles=articles)
response = make_response(rss)
response.mimetype = 'application/xml'
return response
複製代碼
渲染一個模板,簡言之就是經過上下文變量來生成HTML
from flask import render_template
@app.route('/')
def index():
greetings = 'Hello world.'
return render_template('index.html', greetings=greetings)
複製代碼
render_template中第一個參數是要渲染的模板文件名,其他參數則是上下文變量
經過mustache語法將上下文變量傳入模板並渲染,同時也支持if、for等控制流語句語法,更高級的有過濾器、模板繼承、宏等
提幾個經常使用的過濾器:
網站的靜態文件放在static文件夾中,經過反向構造url訪問
url_for('static', filename='style.css')
複製代碼
如下是request所封裝的幾個最經常使用的參數,所有參數請點這裏
request.args # GET請求的查詢字符串
request.cookies # cookies信息
request.files # 請求上傳的文件
request.form # POST請求的表單數據
request.headers # 請求頭
request.method # 請求類型
request.path # 請求路徑
request.referrer # 請求發源地址
request.remote_addr # 用戶的ip地址
request.get_json() # 獲取api的json數據
複製代碼
工廠模式使得app在建立之時能同時完成如下步驟:加載配置,初始化擴展,註冊藍本,註冊shell上下文,以及註冊錯誤處理函數等
def create_app(config_name=None):
if config_name is None:
config_name = os.getenv('FLASK_CONFIG', 'development')
app = Flask(__name__)
app.config.from_object(config[config_name])
register_blueprints(app)
register_extensions(app)
register_shell_context(app)
register_errors(app)
return app
def register_extensions(app):
debugtoolbar.init_app(app)
...
def register_blueprints(app):
app.register_blueprint(main_bp)
...
def register_shell_context(app):
@app.shell_context_processor
def make_shell_context():
return {'db': db, ...}
def register_errors(app):
@app.errorhandler(400)
def bad_request(e):
return render_template('errors/400.html'), 400
...
複製代碼
用來實現模塊化編程
例如一個app(名字叫flasky)一般會有如下的文件夾結構
├─flasky
│ ├─blueprints
│ ├─static
│ │ └─css
│ └─templates
│ ├─auth
│ ├─errors
│ ├─main
│ └─user
└─tests
複製代碼
其中blueprints就是藍本文件夾,裏面存放着和templates對應的4個藍本
├─blueprints
│ auth.py
│ main.py
│ user.py
│ __init__.py
複製代碼
這4個藍本中__init__.py負責Python的包結構,其他三個則是app的3個功能模塊:認證、主頁、用戶
以auth.py爲例
from flask import Blueprint, ...
...(此處省略了一堆import)
bp = Blueprint('auth', __name__)
@bp.route('/login', methods=['GET', 'POST'])
def login():
...
@bp.route('/register', methods=['GET', 'POST'])
def register():
...
@bp.route('/logout')
def log_out():
...
複製代碼
藍本也能夠註冊路由,若是反向構造url的話就得加上藍本名,好比url_for('auth.login')
藍本的註冊應在工廠函數中執行,而且每一個藍本能夠經過url_prefix來給url添加前綴
API返回的通常都是json,故在每一個視圖函數中調用jsonify將dict序列化爲json
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/')
def index():
return jsonify({'message': 'Hello World!'})
@app.route('/foo')
def foo():
return jsonify({'message': 'Hello foo!'})
複製代碼
但其實不必這麼作,由於flask的Response是能夠定製的
flask的app實例提供了response_class屬性,默認是Response
繼續查照定義,發現Response其實繼承了werkzeug裏的BaseResponse
經過查閱BaseResponse,咱們能夠重載Response的force_type類方法,將類型爲dict的response直接jsonify,而且無需在每一個視圖函數中都顯式調用jsonify函數了
from flask import Flask, jsonify, Response
class JSONResponse(Response):
@classmethod
def force_type(cls, response, environ=None):
if isinstance(response, dict):
response = jsonify(response)
return super().force_type(response, environ)
app = Flask(__name__)
app.response_class = JSONResponse
@app.route('/')
def index():
return {'message': 'Hello World!'}
@app.route('/foo')
def foo():
return {'message': 'Hello foo!'}
複製代碼
固然,你也能夠相似地強制響應格式爲xml,RSSHub就是這麼實現的
from flask import Flask, Response
class XMLResponse(Response):
def __init__(self, response, **kwargs):
if 'mimetype' not in kwargs and 'contenttype' not in kwargs:
if response.startswith('<?xml'):
kwargs['mimetype'] = 'application/xml'
return super().__init__(response, **kwargs)
app = Flask(__name__)
app.response_class = XMLResponse
@bp.route('/feed')
def rss_feed():
from rsshub.spiders.feed import ctx
return render_template('main/atom.xml', ctx())
複製代碼
有的時候你但願把某種邏輯抽象成一個函數,使得多個頁面能共用,那麼就要定義全局模板函數了
有的網站的排序功能是這麼實現的:經過在url的查詢字符串中追加sort(要排序的字段)和order(升序仍是降序)參數,在視圖函數中獲取這2個參數並進行相應的排序處理
要實現這個功能,能夠編寫2個全局函數:1個負責修改url的查詢字符串,另外一個負責處理給查詢排序
@bp.app_template_global()
def modify_querystring(**new_values):
args = request.args.copy()
for k, v in new_values.items():
args[k] = v
return f'{request.path}?{url_encode(args)}'
@bp.app_template_global()
def get_article_query():
sort_key = request.args.get('s', 'date')
order = request.args.get('o', 'desc')
article_query = Article.query
if sort_key:
if order == 'asc':
article_query = article_query.order_by(db.asc(sort_key))
else:
article_query = article_query.order_by(db.desc(sort_key))
return article_query
複製代碼
咱們都知道flask的路由映射是存儲在app.url_map中的,所以查閱官方文檔有關url_map的部分,就能輕鬆實現
from flask import Flask
from urllib.parse import unquote
from werkzeug.routing import BaseConverter
class ListConverter(BaseConverter):
def __init__(self, url_map, separator='+'):
super().__init__(url_map)
self.separator = unquote(separator)
def to_python(self, value): # 把路徑轉換成一個Python對象,好比['python', 'javascript', 'sql']
return value.split(self.separator)
def to_url(self, values): # 把參數轉換成符合URL的形式,好比/python+javascript+sql/
return self.separator.join(BaseConverter.to_url(self, value) for value in values)
app = Flask(__name__)
app.url_map.converters['list'] = ListConverter
@app.route('/list1/<list:var>/')
def list1(var):
return f'Separator: + {var}'
@app.route('/list2/<list(separator=u"|"):var>/')
def list2(var):
return f'Separator: | {var}'
複製代碼
官方文檔僅僅用了逗號分隔符,而在這裏咱們經過添加了separator屬性來實現了自定義分隔符 訪問以下連接體驗下效果
http://localhost:5000/list1/python+javascript+sql
http://localhost:5000/list2/python|javascript|sql
複製代碼