一、目錄結構html
目錄:python
Flask程序通常保存在名爲app的包中sql
migrations文件夾包含數據庫遷移腳本shell
單元測試編寫在test包中數據庫
venv文件夾包含python虛擬環境flask
文件:bootstrap
requirements.txt 列出來了全部依賴包,便於在其餘電腦中從新生成相同的虛擬環境session
config.py 存儲配置app
manage.py 用於啓動程序以及其餘的程序任務函數
二、配置選項
config.py
原先採用的是在hello.py中簡單的字典狀結構配置,如今使用層次結構的配置類
import os basedir = os.path.abspath(os.path.dirname(__file__)) class Config: SECRET_KEY = '123456' SQLALCHEMY_COMMIT_ON_TEARDOWN = True FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]' FLASKY_MAIL_SENDER = '*****@qq.com' FLASKY_ADMIN = '*****@qq.com' @staticmethod def init_app(app): pass class DevelopmentConfig(Config): DEBUG = True MAIL_SERVER = 'smtp.qq.com' MAIL_PORT = 465 MAIL_USE_TLS = False MAIL_USE_SSL = True MAIL_USERNAME = os.environ.get('MAIL_USERNAME') MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD') SQLALCHEMY_DATABASE_URI = 'sqlite:///data-dev.sqlite' class TestingConfig(Config): TESTING = True SQLALCHEMY_DATABASE_URI = 'sqlite:///data-test.sqlite' class ProductionConfig(Config): SQLALCHEMY_DATABASE_URI = 'sqlite:///data.sqlite' config = { 'development': DevelopmentConfig, 'testing': TestingConfig, 'production': ProductionConfig, 'default': DevelopmentConfig }
基類config.py中包含通用配置,子類分別電儀了專用的配置,某些敏感的配置能夠從環境變量中導入
SQLALCHEMY_DATABASE_URI變量都被指定了不一樣的值,程序能夠在不一樣配置環境下運行,每一個環境使用不一樣的數據庫
配置類能夠定義init_app()類方法,其參數是程序實例,能夠執行對當前環境的配置初始化。本例中,基類Config中的init_app()方法爲空
在這個配置腳本末尾,config字典中註冊了不一樣的配置環境,並且還註冊了一個默認的配置(本例的開發環境)
三、程序包
程序包用來保存程序的全部代碼、模板和靜態文件。能夠直接稱爲app,若有需求,也可以使用專用名稱。templates和static文件夾是程序包的一部分,數據庫模型和電子郵件支持含函數也被移到這個包中
本例中將原先templates下的文件轉移到app/templates下
四、使用程序工廠函數
app/__init__.py 程序包的構造文件
create_app()函數就是程序的工廠函數,接受一個函數,是程序使用的配置名。配置名在config.py文件中定義,其中保存的配置可使用Flask.app.config配置對象提供的from_object()方法直接導入程序。至於配置對象,則能夠經過名字從config字典中選擇,程序配置完成後,就能初始化擴展了,在以前建立的擴展對象上調用init_app()能夠完成初始化過程
from flask import Flask, render_template from flask_bootstrap import Bootstrap from flask_mail import Mail from flask_moment import Moment from flask_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
五、在藍本中實現程序功能
直接使用app.route修飾器定義路由,在程序運行時才建立,只有調用create_app以後才能使用app.route修飾器。
在藍本中定義的路由處於休眠狀態,直到藍本註冊到程序上後,路由才真正成爲程序的一部分。使用位於全局做用域中的藍本時,定義路由的方法幾乎和單腳本程序同樣,爲獲取最大的靈活性,程序包中建立了一個子包用於保存藍本
建立藍本
app/main/__init__.py
實例化Blueprint類對象建立藍本:參數1,藍本的名字和藍本所在的包或模塊;參數2,使用__name__就好
程序的路由保存在app/main/__init__.py 模塊中,錯誤處理程序保存在app/main/errors.py模塊中,導入這兩個模塊就能把路由和錯誤處理程序與藍本關聯起來
注:
這些模塊在app/main/__init__.py 腳本的末尾導入,這是爲了不循環導入依賴,由於在views.py和errors.py中還要導入藍本main
from flask import Blueprint main = Blueprint('main', __name__) from . import views, errors
六、註冊藍本
app/__init__.py
藍本在工廠函數create_app()中註冊到程序中
from flask import Flask, render_template from flask_bootstrap import Bootstrap from flask_mail import Mail from flask_moment import Moment from flask_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['default']) config['default'].init_app(app) bootstrap.init_app(app) mail.init_app(app) moment.init_app(app) db.init_app(app) #註冊藍本 from .main import main as main_blueprint app.register_blueprint(main_blueprint) #附加路由和自定義的錯誤頁面 return app
藍本中的錯誤處理程序
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
在藍本中編寫錯誤處理程序有所不一樣,若是使用errorhandler修飾器,那麼只有藍本中的錯誤才能觸發處理程序,要想註冊程序全局的錯誤處理程序,必須使用app_errorhandler
藍本中定義的程序路由
app/main/view.py
在藍本中編寫視圖函數不一樣點
(1)和錯誤處理程序同樣,路由修飾器由藍本提供
(2)url_for()函數的用法不一樣,在程序的路由中,url_for()第一個參數是視圖函數的名稱,在藍本中,Flask會爲藍本中的所有端點加上一個命名空間,這樣能夠在不一樣的藍本中使用相同的端點名定義視圖函數,而不會產生衝突。命名空間名稱(Blueprint構造函數的第一個參數)就是藍本的名稱,因此視圖函數index()註冊的端點名是main.index,其URL使用url_for('main.index')獲取。
url_for('.index')是藍本中一種簡寫的端點形式,但在跨藍本的重定向必須使用帶有端點命名空間的端點名
from datetime import datetime from flask import render_template, session, redirect, url_for from . import main from .forms import NameForm from .. import db from ..models import User @main.route('/', methods=['GET', 'POST']) 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 app.config['FLASKY_ADMIN']: #send_mail(app.config['FLASKY_ADMIN'], 'New User', 'mail/new_user', user=user) else: session['known'] = True session['name'] = form.name.data form.name.data = '' return redirect(url_for('.index')) return render_template('index.html', form = form, name = session.get('name'), known = session.get('known', False), current_time=datetime.utcnow())
七、啓動腳本
頂級問價夾中的manage.py文件用於啓動程序
manage.py
先讀取配置名:若是已經定義了環境變量FLASK_CONFIG,則從衆讀取配置名;不然使用默認配置
初始化Flask-Script、Flask-Migrate和爲python shell定義的上下文
#!/usr/bin/env python import os from app import create_app, db from app.models import User, Role from flask_script import Manager, Shell from flask_migrate import Migrate, MigrateCommand app = create_app(os.getenv('default')) manager = Manager(app) migrate = Migrate(app, db) def make_shell_context(): return dict(app=app, db=db, User=User, Role=Role) manager.add_command("shell", Shell(make_context=make_shell_context)) manager.add_command('db', MigrateCommand) if __name__ == '__main__': manager.run()
八、需求文件
更新需求文件
pip freeze >requirements.txt
九、單元測試
tests/test_basics.py
import unittest from flask import current_app from app import create_app, db class BasicsTestCase(unittest.TestCase): def setUp(self): self.app = create_app('testing') self.app_context = self.app.app_context() self.app_context.push() db.create_all() def tearDown(self): db.session.remove() db.drop_all() self.app_context.pop() def test_app_exists(self): self.assertFalse(current_app is None) def test_app_is_testing(self): self.assertTrue(current_app.config['TESTING'])
十、啓動單元測試
manage.py
manager.command修飾器讓自定義命令變得簡單。修飾函數名就是命令名,函數的文檔字符串會顯示在幫助信息中。test()函數的定義體中調用了unittest包提供的測試運行函數
#!/usr/bin/env python import os from app import create_app, db from app.models import User, Role from flask_script import Manager, Shell from flask_migrate import Migrate, MigrateCommand app = create_app(os.getenv('default')) manager = Manager(app) migrate = Migrate(app, db) def make_shell_context(): return dict(app=app, db=db, User=User, Role=Role) manager.add_command("shell", Shell(make_context=make_shell_context)) manager.add_command('db', MigrateCommand) #啓動單元測試 @manager.command def test(): """Run the unit tests.""" import unittest tests = unittest.TestLoader().discover('tests') unittest.TextTestRunner(verbosity=2).run(tests) if __name__ == '__main__': manager.run()
十一、運行單元測試
python manage.py test
不知道咋回事,頭疼,待查
十二、建立數據庫
python manage.py db upgrade