九、大型程序的結構

一、目錄結構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

 

 

相關文章
相關標籤/搜索