1. 程式人生 > >Flask從入門到精通之大型程序的結構一

Flask從入門到精通之大型程序的結構一

bootstrap jinja 運行 body 精通 強制 上下文 htm Coding

  盡管在單一腳本中編寫小型Web 程序很方便,但這種方法並不能廣泛使用。程序變復雜後,使用單個大型源碼文件會導致很多問題。不同於大多數其他的Web 框架,Flask 並不強制要求大型項目使用特定的組織方式,程序結構的組織方式完全由開發者決定。在本節,我們將介紹一種使用包和模塊組織大型程序的方式。

一.項目結構

  Flask 程序的基本結構如下所示:  

|-blogs
    |-app/
        |-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

  這種結構有4 個頂級文件夾:

  •   Flask程序一般都保存在名為app 的包中
  •   和之前一樣,migrations文件夾包含數據庫遷移腳本
  •   單元測試編寫在tests包中
  •   和之前一樣,venv 文件夾包含Python 虛擬環境

  同時還創建了一些新文件:

  •   requirements.txt列出了所有依賴包,便於在其他電腦中重新生成相同的虛擬環境
  •   config.py 存儲配置
  •   manage.py用於啟動程序以及其他的程序任務

  為了幫助你完全理解這個結構,下面幾節講解把前面介紹的hello.py 程序轉換成這種結構的過程

二.配置選項

  程序經常需要設定多個配置。這方面最好的例子就是開發、測試和生產環境要使用不同的數據庫,這樣才不會彼此影響。我們不再使用hello.py 中簡單的字典狀結構配置,而使用層次結構的配置類。config.py 文件的內容如下示例所示:

import os
basedir = os.path.abspath(os.path.dirname(__file__))


class Config:
    SECRET_KEY = os.environ.get(SECRET_KEY) or hard to guess string
    MAIL_SERVER = os.environ.get(MAIL_SERVER, smtp.googlemail.com)
    MAIL_PORT = int(os.environ.get(MAIL_PORT, 587))
    MAIL_USE_TLS = os.environ.get(MAIL_USE_TLS, true).lower() in         [true, on, 1]
    MAIL_USERNAME = os.environ.get(MAIL_USERNAME)
    MAIL_PASSWORD = os.environ.get(MAIL_PASSWORD)
    FLASKY_MAIL_SUBJECT_PREFIX = [Flasky]
    FLASKY_MAIL_SENDER = Flasky Admin <[email protected]>
    FLASKY_ADMIN = os.environ.get(FLASKY_ADMIN)
    SSL_REDIRECT = False
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    SQLALCHEMY_RECORD_QUERIES = True
    FLASKY_POSTS_PER_PAGE = 20
    FLASKY_FOLLOWERS_PER_PAGE = 50
    FLASKY_COMMENTS_PER_PAGE = 30
    FLASKY_SLOW_DB_QUERY_TIME = 0.5

    @staticmethod
    def init_app(app):
        pass


class DevelopmentConfig(Config):
    DEBUG = True
    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://
    WTF_CSRF_ENABLED = False


class ProductionConfig(Config):
    SQLALCHEMY_DATABASE_URI = os.environ.get(DATABASE_URL) or         sqlite:/// + os.path.join(basedir, data.sqlite)

    @classmethod
    def init_app(cls, app):
        Config.init_app(app)

        # email errors to the administrators
        import logging
        from logging.handlers import SMTPHandler
        credentials = None
        secure = None
        if getattr(cls, MAIL_USERNAME, None) is not None:
            credentials = (cls.MAIL_USERNAME, cls.MAIL_PASSWORD)
            if getattr(cls, MAIL_USE_TLS, None):
                secure = ()
        mail_handler = SMTPHandler(
            mailhost=(cls.MAIL_SERVER, cls.MAIL_PORT),
            fromaddr=cls.FLASKY_MAIL_SENDER,
            toaddrs=[cls.FLASKY_ADMIN],
            subject=cls.FLASKY_MAIL_SUBJECT_PREFIX +  Application Error,
            credentials=credentials,
            secure=secure)
        mail_handler.setLevel(logging.ERROR)
        app.logger.addHandler(mail_handler)


class HerokuConfig(ProductionConfig):
    SSL_REDIRECT = True if os.environ.get(DYNO) else False

    @classmethod
    def init_app(cls, app):
        ProductionConfig.init_app(app)

        # handle reverse proxy server headers
        from werkzeug.contrib.fixers import ProxyFix
        app.wsgi_app = ProxyFix(app.wsgi_app)

        # log to stderr
        import logging
        from logging import StreamHandler
        file_handler = StreamHandler()
        file_handler.setLevel(logging.INFO)
        app.logger.addHandler(file_handler)


class DockerConfig(ProductionConfig):
    @classmethod
    def init_app(cls, app):
        ProductionConfig.init_app(app)

        # log to stderr
        import logging
        from logging import StreamHandler
        file_handler = StreamHandler()
        file_handler.setLevel(logging.INFO)
        app.logger.addHandler(file_handler)


class UnixConfig(ProductionConfig):
    @classmethod
    def init_app(cls, app):
        ProductionConfig.init_app(app)

        # log to syslog
        import logging
        from logging.handlers import SysLogHandler
        syslog_handler = SysLogHandler()
        syslog_handler.setLevel(logging.INFO)
        app.logger.addHandler(syslog_handler)


config = {
    development: DevelopmentConfig,
    testing: TestingConfig,
    production: ProductionConfig,
    heroku: HerokuConfig,
    docker: DockerConfig,
    unix: UnixConfig,

    default: DevelopmentConfig
}

  基類Config 中包含通用配置,子類分別定義專用的配置。如果需要,你還可添加其他配置類。為了讓配置方式更靈活且更安全,某些配置可以從環境變量中導入。例如,SECRET_KEY 的值,這是個敏感信息,可以在環境中設定,但系統也提供了一個默認值,以防環境中沒有定義。在3 個子類中,SQLALCHEMY_DATABASE_URI 變量都被指定了不同的值。這樣程序就可在不同的配置環境中運行,每個環境都使用不同的數據庫。配置類可以定義init_app() 類方法,其參數是程序實例。在這個方法中,可以執行對當前環境的配置初始化。現在,基類Config 中的init_app() 方法為空。在這個配置腳本末尾,config 字典中註冊了不同的配置環境,而且還註冊了一個默認配置。

三.啟動腳本

  頂級文件夾下的manage.py 文件用於啟動程序。腳本內容如下:

#!/usr/bin/env python
import os
from app import create_app, db
from app.models import User, Role
from flask.ext.script import Manager, Shell
from flask.ext.migrate import Migrate, MigrateCommand
app = create_app(os.getenv(FLASK_CONFIG) or 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()

  這個腳本先創建程序。如果已經定義了環境變量FLASK_CONFIG,則從中讀取配置名;否則使用默認配置。然後初始化Flask-Script、Flask-Migrate 和為Python shell 定義的上下文。出於便利,腳本中加入了shebang 聲明,所以在基於Unix 的操作系統中可以通過./manage.py 執行腳本,而不用使用復雜的python manage.py。

四.需求文件

  程序中必須包含一個requirements.txt 文件,用於記錄所有依賴包及其精確的版本號。如果要在另一臺電腦上重新生成虛擬環境,這個文件的重要性就體現出來了,例如部署程序時使用的電腦。pip 可以使用如下命令自動生成這個文件:

pip freeze >requirements.txt

  安裝或升級包後,最好更新這個文件。需求文件的內容示例如下:

alembic==0.9.3
bleach==2.0.0
blinker==1.4
click==6.7
dominate==2.3.1
Flask==0.12.2
Flask-Bootstrap==3.3.7.1
Flask-HTTPAuth==3.2.3
Flask-Login==0.4.0
Flask-Mail==0.9.1
Flask-Migrate==2.0.4
Flask-Moment==0.5.1
Flask-PageDown==0.2.2
Flask-SQLAlchemy==2.2
Flask-WTF==0.14.2
html5lib==0.999999999
itsdangerous==0.24
Jinja2==2.9.6
Mako==1.0.7
Markdown==2.6.8
MarkupSafe==1.0
python-dateutil==2.6.1
python-dotenv==0.6.5
python-editor==1.0.3
six==1.10.0
SQLAlchemy==1.1.11
visitor==0.1.3
webencodings==0.5.1
Werkzeug==0.12.2
WTForms==2.1

  如果你要創建這個虛擬環境的完全副本,可以創建一個新的虛擬環境,並在其上運行以下命令:

pip install -r requirements.txt

五.創建數據庫

  不管從哪裏獲取數據庫URL,都要在新數據庫中創建數據表。如果使用Flask-Migrate 跟蹤遷移,可使用如下命令創建數據表或者升級到最新修訂版本

python manage.py db upgrade

Flask從入門到精通之大型程序的結構一