Flask介紹
對于Flask大家很熟悉了,現在主流Python的web框架,除了Django就數Flask了。
Django不用多說,集合了orm、template模板引擎、后臺管理系統、參數驗證、路由系統、用戶認證和其他一些工具的,方便快速建站,主要構建了ORM和復寫ADMIN模塊,就可以快速生成網站和后臺增上改查和一些其他的定制化開發。
Flask是一個輕量級的可定制框架,使用Python語言編寫,較其他同類型框架更為靈活、輕便、安全且容易上手。它可以很好地結合MVC模式進行開發,開發人員分工合作,小型團隊在短時間內就可以完成功能豐富的中小型網站或Web服務的實現。另外,Flask還有很強的定制性,用戶可以根據自己的需求來添加相應的功能,在保持核心功能簡單的同時實現功能的豐富與擴展,其強大的插件庫可以讓用戶實現個性化的網站定制,開發出功能強大的網站。
筆者更傾向于Flask這個框架,因為它更加透明,更能掌控項目的質量。
使用過的插件
Flask的插件管理是一個很不錯的特性,我們利用它可以在啟動web服務之前加載我們所需要的插件,一下是筆者經常用到的:
- flask-restful
flask的restapi開發框架,有參數校驗,路由攔截等功能。
- flask-sqlalchemy
Sqlalchemy的flask插件,這個插件作者擴展了針對與session中的query的分頁功能,可以使用SQLALCHEMY_BINDS進行多個數據庫的綁定,可以配置連接池,連接池過期時間。
- MySQLclient或者pymysql
Sqlalchemy連接mysql的引擎,不過pymysql需要加入一句話:
import pymysql
pymysql.install_as_MySQLdb()
- marshmallow
Sqlalcemy序列化數據利器,可以使用它針對query返回的QuerySet結果集合進行序列化成dict 數據。
- flask-cors
開發API會遇到跨域訪問的問題,可以就是客戶端OPTIONS的請求,先探知服務器允不允許跨域訪問,允許會返回header中允許那些方法進行請求服務器(GET、POST、DELETE、PUT等),這個插件可以通過簡單的配置,讓您的服務允許跨域訪問,不過在生產環境,還是推薦使用Nginx的好,能夠使用lua語言進行更加嚴格的控制。
- flask-login或者flask-jwt-extended
這兩個都是用戶認證用到的插件,使用session控制,選擇flask-login,使用jwt使用flask-jwt-extended。
Flask框架
下面簡單說一下flask的框架,flask框架是基于Werkzeug和Jinja2兩大基礎框架,Werkzeug是基于WSGI協議的框架,已經有了基本的路由、數據結構、請求和響應等功能,在此之上Flask擴展了一些特性:
- request
Flask利用Werkzeug的Local、LocalStack和LocalProxy,實現了AppContext和RequestContext的線程或者協程安全功能,也就是:
- 當請求過來,Flask將app_context和request,這兩個對象壓入一個線程號或者協程號對應的棧中,說白了就是dict字典,線程或者攜程號有get_indent內置方法獲得
- 請求過程中,由棧彈出上次壓入棧中的app_context和request
- 請求結束釋放空間
另外說明flask-sqlalchemy利用線程或者攜程號有get_indent,可以使用scope_session功能,將session隔離,它是線程或者協程安全的,也和flask的請求保持一致。
- endpoint
使得每個view方法,有個名稱,這個名稱是唯一的,它能夠通過這個名稱找到這個視圖方法,也就能夠獲得路由。
這里數據結構簡單地說就是一個map類型,一個名稱唯一對應一個視圖方法,不允許相同的endpoint出現,這個應用在使用url_for這個方法上:
- 程序中使用url_for進行路由跳轉
- template模板總是用url_for查找對應的路由
這樣可以靈活根據名稱去使用路由,而不是硬編碼到程序中非常死板,如果說我們修改了一個url的地址,只是微小的修改,其他引用這個路由的地方也需要修改,我們可能忘記了使用它的地方,這時url_for非常好的解決了這個問題。
- extentions
擴展應用,提供了兩種方式,一種是直接加載,之類用生成對象例如CORS(flask_app),也可以懶加載,在創建應用之前定義對象,創建flask_app時加載插件,例如flask-sqlalchemy
from flask import Flask
from flask_sqlalcemy import Sqlalchemy
db = Sqlalchemy()
def create_app():
"""
創建Flask app
"""
f_app = Flask()
f_app.config.from_object("core.settings")
db.init_app(app=f_app)
return f_app
這樣的形式,我們可以使用全局這個db對象了。
開發項目目錄結構
我們可以借鑒django的目錄形式進行組織項目的框架,例如:
core文件夾基本這個樣子,app 是項目Flask應用初始化,settings是配置信息,應用是user和config等,static存放靜態文件,templates是模板目錄,requirements.txt里面配置依賴,run.py是調試使用的啟動入口。
特別的一個應用目錄為:
其中,__init__.py是module的文件,dbis.py里面都是面向數據操作的方法,models.py里面都是ORM的定義,urls.py里面是路由配置,validators.py存放參數校驗方法,views.py存放視圖方法,serializers.py存放序列化的方法,dbi_services.py存放構造views.py中使用的數據類型。
我們如何將他們和Flask的應用聯系起來呢,可以進行如下操作:
生成Flask的應用代碼:
import os
from flask_sqlalchemy import SQLAlchemy
from flask_bootstrap import Bootstrap
from flask_url.conf import FlaskUrlConf
from flask_wtf.csrf import CSRFProtect
from flask import Flask
db = SQLAlchemy()
def create_app(import_name):
"""
創建Flask應用
:param import_name: [str] 模塊名稱
:return:
"""
f_app = Flask(import_name)
# 根據環境變量加載配置信息
env_dict = {
"DEV": "core.dev_settings",
"ONLINE": "core.online_settings"
}
env = os.environ.get("PRO_ENV", "DEV")
f_app.config.from_object(env_dict.get(env, "core.dev_settings"))
FlaskUrlConf(app=f_app)
db.init_app(app=f_app)
CSRFProtect(app=f_app)
print(f_app.url_map)
return f_app
app = create_app(__name__)
其中使用了FlaskUrlConf(app=f_app)這個,是我自己構造的一個插件,貼出這段代碼:
from flask import Flask
from flask.blueprints import Blueprint
from flask_url.utils import get_mod_attr
def path(rule, endpoint, view_func, **options):
return Url(rule=rule, endpoint=endpoint, view_func=view_func, **options)
class Url:
rule = None
endpoint = None
view_func = None
options = {}
def __init__(self, rule, endpoint, view_func, **options):
self.rule = rule
self.endpoint = endpoint
self.view_func = view_func
self.options = options
class FlaskUrlConf:
def __init__(self, app: Flask = None):
if app:
self.init_app(app=app)
def init_app(self, app: Flask):
install_apps = app.config.get("INSTALL_APPS", [])
for app_str in install_apps:
mod_path = f"{app_str}.urls"
try:
urls = get_mod_attr(mod_path, "urls")
b = Blueprint(import_name=mod_path, name=app_str)
for url in urls:
if isinstance(url, Url):
b.add_url_rule(
rule=url.rule,
endpoint=url.endpoint,
view_func=url.view_func,
**url.options
)
app.register_blueprint(b)
except ImportError as e:
app.logger.warn(f"Import module {mod_path} err {e}")
except IndexError as e:
app.logger.warn(f"Split module {mod_path} attr error {e}")
except AttributeError as e:
app.logger.warn(f"Get attr from url module {mod_path}, error {e}")
其核心思想是通過INSTALL_APPS這個配置,將各級模塊兒應用下的urls.py中的urls列表進行加載,urls.py示例:
from flask_url.conf import path
from article import views
urls = [
path(rule="/article/page/<int:a_id>/", view_func=views.get_article, endpoint="page")
]
那我們就完成了MVC的項目目錄結構的搭建。
部署
最后我們再來談一下flask應用的部署,我們可以使用gunicorn或者uwsgi進行部署,通過nginx進行代理即可。
uwsgi配置示例:
uwsgi]
uid = root
gid = root
master = true
processes = 1
listen = 10
socket = 127.0.0.1:9000
pidfile = /uwsgi.pid # 你的路徑
vacuum = true
daemonize = /uwsgi.log # 你的日志路徑
chdir = ${你的項目路徑}
home=${你的env的路徑}
buffer-size = 32768 # 可以修改允許最大交換數據
module = core.wsgi # 你的 wsgi模塊路徑
callable = app # 選擇你的應用,Flask的應用
http-websockets = true # 是否啟用web socket
gevent = 10 # 啟用協程
async = 10
nginx配置示例
server {
listen 8888;
charset UTF-8;
location ^~ /api/ {
include uwsgi_params;
uwsgi_pass http://127.0.0.1:9000;
client_max_body_size 100m;
}
}
以上是總結的一些內容跟大家分享,由于篇幅有限有些細節沒有說清,大家可以在評論區提問。希望大家多多支持。