flask是python的一個web應用框架,django很多人聽過,flask比較少見,連創始人一開始寫出來只是個笑話而已,對python3支持不太好,flask的好處是微應用,有需求的插件才使用,精簡而靈活,由于靈活,要怎么創建一個項目要自己去思考怎么搭建,不像django幫你做完項目搭建的步驟。個人覺得怎么搭建一個項目也是一個學習后臺架構的過程。這里不會詳細介紹flask和flask各個模塊的功能,留作思考與學習。
|-flasky
|-apps/
|-templates/
|-static/
|-main/
|-__init__.py
|-errors.py
|-forms.py
|-views.py
|-__init__.py
|-email.py
|-models.py
|-migrations/
|-tests/
|-__init__.py
|-test*.py
|-venv/
|-requirements.txt
|-config.py
|-manage.py
|-command.py
項目有八個頂層目錄:
- app的目錄下是放Flask應用
- migrations目錄包含數據庫遷移腳本
- test目錄下是放單元測試
- venv是Python虛擬環境
- requirements.txt是Python運行的依賴包列表
- config.py是配置設置腳本
- manage.py 用于啟動應用程序和其他應用程序任務
- command.py 控制
app層
app下面有main、static、templates三個文件夾以及init.py、email.py、models.py
-
main文件夾用來保存藍本,此文件夾下 init.py文件里面創建藍本,(藍本和程序類似,也可以定義路由。不同的是,在藍本中定義的路由處于休眠狀態,直到藍本注冊到程序上后,路由才真正成為程序的一部分。)main文件夾下views.py用來保存程序的路由,errors.py用來處理錯誤,forms.py是存放表單定義。
-
init.py
和路由相關聯的藍圖都在休眠狀態,只有當藍圖在應用中被注冊后,此時的路由才會成為它的一部分。使用定義在全局作用域下的藍圖,定義應用程序的路由就幾乎可以和單腳本應用程序一樣簡單了。
藍圖可以定義在一個文件或一個包中與多個模塊一起創建更結構化的方式。為了追求最大的靈活性,可以在應用程序包中創建子包來持有藍圖。
# app/main/ _init__.py:創建藍圖_ from flask import Blueprint main = Blueprint('main', __name__) from . import views, errors
?
藍圖是通過實例化
Blueprint
類對象來創建的。這個類的構造函數接收兩個參數:藍圖名和藍圖所在的模塊或包的位置。與應用程序一樣,在大多數情況下,對于第二個參數值使用Python的__name__
變量。應用程序的路由都保存在
app/main/views.py
模塊內部,而錯誤處理程序則保存在app/main/errors.py
中。導入這些模塊可以使路由、錯誤處理與藍圖相關聯。重要的是要注意,在app/init.py
腳本的底部導入模塊要避免循環依賴,因為view.py
和errors.py
都需要導入main
藍圖。# app/ _init__.py:藍圖注冊_ def create_app(config_name): from .main import main as main_blueprint app.register_blueprint(main_blueprint) return app
-
errors.py
在藍圖中寫錯誤處理的不同之處是,如果使用了
errorhandler
裝飾器,則只會調用在藍圖中引起的錯誤處理。而應用程序范圍內的錯誤處理則必須使用app_errorhandler
。# app/main/errors.py:藍圖的錯誤處理 from flask import render_template from . import main @main.app_errorhandler(404) def page_not_found(e): return render_template('404.html'), 404 @main.app_errorhandler(500) def internal_server_error(e): return render_template('500.html'), 500
-
views.py
# app/main/views.py:帶有藍圖的應用程序路由 from datetime import datetime from flask import render_template, session, redirect, url_for from . import main from .forms import NameForm from ..models import User @main.route('/',methods = ['POST','GET']) #請求方式不管是post還是get都執行這個視圖函數 def index(): form = NameForm() #表單實例 if form.validate_on_submit(): #提交按鈕是否成功點擊 # 從數據庫中查找和表單數據一樣的數據,如果有,取第一個數據 user = User.query.filter_by(username = form.name.data).first() if user is None: #如果數據庫中沒有對應的數據 user = User(username = form.name.data) #在數據庫中對應的表中創建數據 db.session.add(user) #加入到用戶會話,以便數據庫進行提交 session['known'] = False #這是一個新用戶 if current_app.config['FLASKY_ADMIN']: #如果收件人已經定義,則調用發送郵件函數 send_email(current_app.config['FLASKY_ADMIN'],'New User','mail/new_user',user = user) flash('The mail has been sent out') else: session['known'] = True #這是一個老用戶 session['name'] = form.name.data #從表單獲取數據 return redirect(url_for('.index')) return render_template('index.html',current_time = datetime.utcnow(), form = form,name=session.get('name'),known
-
在藍圖中寫視圖函數有兩大不同點。第一,正如之前的錯誤處理一樣,路由裝飾器來自于藍圖。第二個不同是url_for()函數的使用。該函數的第一個參數為路由節點名,它給基于應用程序的路由指定默認視圖函數。例如,單腳本應用程序中的index()視圖函數的URL可以通過url_for('index')來獲得。
Flask名稱空間適用于來自藍圖的所有節點,這樣多個藍圖可以使用相同節點定義視圖函數而不會產生沖突。名稱空間就是藍圖名(Blueprint構造函數中的第一個參數),所以index()視圖函數注冊為main.index且它的URL可以通過url_for(main.index)獲得。
rl_for()函數同樣支持更短格式的節點,省略藍圖名,例如url_for('.index')。有了這個,就可以這樣使用當前請求的藍圖了。這實際意味著相同藍圖內的重定向可以使用更短的形式,如果重定向跨藍圖則必須使用帶名稱空間的節點名。
完成了應用程序頁面更改,表單對象也保存在app/main/forms.py
模塊中的藍圖里面。
static**存放靜態文件
templates用來存放響應的html文件,mail子文件里面的用來保存發送郵件所需的.html和.txt文件
-
init.py文件里面包含create_app()函數,已經app的各種初始化。
在單個文件中創建應用程序的方式非常方便,但是它有一個大缺點。因為應用程序創建在全局范圍,沒有辦法動態的適應應用配置的更改:腳本運行時,應用程序實例已經創建,所以它已經來不及更改配置。解決這一問題的方法就是將應用程序放入一個工廠函數中來延遲創建,這樣就可以從腳本中顯式的調用。這不僅給腳本充足的時間來設置配置,也能用于創建多個應用程序實例。
這個構造函數導入大部分當前需要使用的擴展,但因為沒有應用程序實例初始化它們,它可以被創建但不初始化通過不傳遞參數給它們的構造函數。
create_app()
即應用程序工廠函數,需要傳入用于應用程序的配置名。配置中的設置被保存在config.py
中的一個類中,可以使用Flask的app.config
配置對象的from_object()
方法來直接導入。配置對象可以通過對象名從config
字典中選出。一旦應用程序被創建且配置好,擴展就可以被初始化。調用擴展里的init_app()
之前先創建并完成初始化工作。# app/ _init__.py:應用程序包構造函數 from flask import Flask, render_template from flask.ext.bootstrap import Bootstrap from flask.ext.mail import Mail from flask.ext.moment import Moment from flask.ext.sqlalchemy import SQLAlchemy from config import config bootstrap = Bootstrap() mail = Mail() moment = Moment() db = SQLAlchemy() def create_app(config_name): app = Flask(__name__) app.config.from_object(config[config_name]) config[config_name].init_app(app) bootstrap.init_app(app) mail.init_app(app) moment.init_app(app) db.init_app(app) return app
?
email.py包含send_email()發送文件函數(異步)
models.py包含User和Role兩個表定義
config.py 應用配置示例
config.py中含有一個基類Config定義,三個繼承類定義DevlopmentConfig、TestingConfig、ProductionConfig和一個字典config
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
FLASKY_MAIL_SENDER = 'Flasky Admin <flasky@example.com>'
FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
@staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config):
DEBUG = True
MAIL_SERVER = 'smtp.googlemail.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data-test.sqlite')
class ProductionConfig(Config):
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
Config
基類包含一些相同配置;不同的子類定義不同的配置。額外配置可以在需要的時候在加入。
為了讓配置更靈活更安全,一些設置可以從環境變量中導入。例如,SECRET_KEY
,由于它的敏感性,可以在環境中設置,但如果環境中沒有定義就必須提供一個默認值。
在三個配置中SQLALCHEMY_DATABASE_URI
變量可以分配不同的值。這樣應用程序可以在不同的配置下運行,每個可以使用不同的數據庫。
配置類可以定義一個將應用程序實例作為參數的init_app()
靜態方法。這里特定于配置的初始化是可以執行的。這里Config
基類實現一個空init_app()
方法。
在配置腳本的底部,這些不同的配置是注冊在配置字典中(config)。將其中一個配置(開發配置)注冊為默認配置。
manage.py
包含app創建,manage、migrate初始化,以及make_shell_context()函數在命令行獲取上下文,避免頻繁導入還有test()函數,用來測試。
# manage.py
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from wsgi import app
from models.base import db
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command("db", MigrateCommand)
if __name__ == "__main__":
manager.run()