第六章 實例項目的描述

盡管在單一腳本中編寫小型WEB程序很方便,但是這種方法并不能廣泛使用。程序變復雜之后,使用單個大型源碼文件會導致后期的維護及擴展困難尚骄。但是不同于其它的WEB框架,F(xiàn)lask并不強制要求大型項目使用特定的組織方式侵续,程序結構的組織方式完全由開發(fā)者決定倔丈。

項目的結構

ousimd/

|-- flask/

    |-- <python虛擬環(huán)境>

|-- app/ <項目的模塊名稱>

    |-- static/ <靜態(tài)文件夾>

    |-- templates/ <HTML模板文件夾>

        |-- main/ <前端藍圖>

            |-- __init__.py

            |-- views.py <路由和視圖函數(shù)文件>

            |-- forms.py <表單類文件, wtforms插件必須項>
        |-- admin/ <后臺管理藍圖>

            |-- __init__.py

            |-- views.py <路由和視圖函數(shù)文件>

            |-- forms.py <表單類文件, wtforms插件必須項>

    |-- __init__.py

    |-- another.py <可選項,根據(jù)需要增加>

    |-- models.py <數(shù)據(jù)庫模型文件>

|-- migrations/ <數(shù)據(jù)庫表關系文件夾,Flask-Migrate遷移數(shù)據(jù)庫時使用>

|-- tests/

    |-- __init__.py

    |-- test*.py <測試文件>

|-- requeirements.txt <插件依賴包>

|-- config.py <項目的配置文件>

|-- manage.py <用于啟動程序以及其它程序任務>

配置選項

在項目中配置config.py

# -*- coding:utf-8 -*-
__author__ = u'東方鶚'

import os
import hashlib

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


class Config(object):
    SECRET_KEY = os.environ.get('SECRET_KEY') or hashlib.new(name='md5', string='ousikeji@#').hexdigest()
    SQLALCHEMY_COMMIT_ON_TEARDOWN = True    

    @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:///' + 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中包含通用配置状蜗,子類分別定義專用的配置需五。如果需要,你還可以添加其它配置類轧坎。

為了讓配置方式更加靈活且安全警儒,某些配置可以從環(huán)境變量中導入。例如眶根,SECRET_KEY的值,這是個敏感信息边琉,可以在環(huán)境中設定属百,但系統(tǒng)也提供了一個默認值,以防環(huán)境中沒有定義变姨。
在3個子類中族扰,SQLALCHEMY_DATABASE_URI變量都被指定了不同的值,這樣程序就可以在不同的配置環(huán)境中運行,每個環(huán)境都使用不同的數(shù)據(jù)庫渔呵。其中SQLALCHEMY_DATABASE_URI變量對應的數(shù)據(jù)庫引擎在 ** 《第七章 使用 Flask 擴展管理數(shù)據(jù)庫》 ** 有詳細介紹怒竿。
配置類可以定義init_app()類方法,其參數(shù)是程序實例扩氢。在這個方法中耕驰,可以執(zhí)行對當前環(huán)境的配置初始化。現(xiàn)在录豺,基類Config中的init_app()方法為空朦肘。

程序包

程序包是用來保存程序的所有代碼、模板和靜態(tài)文件双饥。我們可以把這個包直接成為app(應用)媒抠,如果有需求,也可使用一個程序專用名字咏花。templates和static文件夾是程序包的一部分趴生,因此這兩個文件夾被移到了app中。數(shù)據(jù)庫模型app/models.py和其他專用支持函數(shù)也被移到這個包中昏翰。

使用工廠函數(shù)

構造文件導入了大多數(shù)正在使用的Flask擴展苍匆。由于尚未初始化所需的程序實例,所以沒有初始化擴展矩父,創(chuàng)建擴展類時沒有向構造函數(shù)傳入?yún)?shù)锉桑。create_app()函數(shù)就是程序的工廠函數(shù),接受一個參數(shù)窍株,是程序使用的配置名民轴。配置類在config.py文件中定義,其中保存的配置可以使用Flask的app.config配置對象提供的from_object()方法直接導入程序球订。至于配置對象后裸,測可以通過名字從config字典中選擇。程序創(chuàng)建并配置好后冒滩,接能初始化擴展了微驶。在之前創(chuàng)建的擴展對象上調用init_app()可以完成初始化過程。

程序包的構造文件app/__init__.py

# -*- coding:utf-8 -*-
__author__ = '東方鶚'

from flask import Flask
from config import config

# db = SQLAlchemy()

def create_app(config_name):
    """ 使用工廠函數(shù)初始化程序實例"""
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app=app)

    # db.init_app(app=app)

    return app

工廠函數(shù)返回創(chuàng)建的程序示例开睡,不過要注意因苹,現(xiàn)在工廠函數(shù)創(chuàng)建的程序還不完成,在今后會陸續(xù)添加和完善篇恒。

使用藍圖

為了獲得最大的靈活性扶檐,程序包中創(chuàng)建一個子包,用于保存藍圖胁艰。

創(chuàng)建 藍圖main和藍圖admin

app/main/__init__.py

# -*- coding:utf-8 -*-
__author__ = '東方鶚'


from flask import Blueprint

main = Blueprint('main', __name__)

# 在末尾導入相關模塊款筑,是為了避免循環(huán)導入依賴智蝠,因為在下面的模塊中還要導入藍本main
from . import views, errors

app/admin/__init__.py

# -*- coding:utf-8 -*-
__author__ = '東方鶚'


from flask import Blueprint

admin = Blueprint('admin', __name__)

# 在末尾導入相關模塊,是為了避免循環(huán)導入依賴奈梳,因為在下面的模塊中還要導入藍本main
from . import views, errors

通過實例化一個Blueprint類對象可以創(chuàng)建藍圖杈湾。這個構造函數(shù)有兩個必須指定的參數(shù):藍圖的名字和藍圖所在的包或模塊。和程序一樣攘须,大多數(shù)情況下第二個參數(shù)使用Python的__name__變量即可漆撞。

藍圖在工廠函數(shù)create_app()中注冊

app/__init__.py中注冊藍圖

def create_app(config_name):
    # ...

    # 注冊藍本 main
    from .main import main as main_blueprint
    app.register_blueprint(main_blueprint, url_prefix='/main')

    # 注冊藍本 admin
    from .admin import admin as admin_blueprint
    app.register_blueprint(admin_blueprint, url_prefix='/admin')

    return app

藍圖的錯誤處理(以藍圖main為例)

app/main/errors.py


# -*- coding:utf-8 -*-
__author__ = '東方鶚'

from flask import render_template, request, jsonify
from . import main


@main.app_errorhandler(403)
def forbidden(e):
    if request.accept_mimetypes.accept_json and \
            not request.accept_mimetypes.accept_html:
        response = jsonify({'error': 'forbidden'})
        response.status_code = 403
        return response
    return render_template('main/errors/403.html'), 403


@main.app_errorhandler(404)
def page_not_found(e):
    if request.accept_mimetypes.accept_json and \
            not request.accept_mimetypes.accept_html:
        response = jsonify({'error': 'not found'})
        response.status_code = 404
        return response
    return render_template('main/errors/404.html'), 404


@main.app_errorhandler(500)
def internal_server_error(e):
    if request.accept_mimetypes.accept_json and \
            not request.accept_mimetypes.accept_html:
        response = jsonify({'error': 'internal server error'})
        response.status_code = 500
        return response
    return render_template('main/errors/500.html'), 500

在藍圖中編寫錯誤處理程序少有不同,如果使用errorhandler修時期阻课,那么只有藍圖中的錯誤才能觸發(fā)處理程序叫挟。要想注冊程序全局的錯誤處理程序,必須使用app_errorhandler限煞。當然抹恳,示例還提供基于json的錯誤提示編寫方法。

藍圖中定義程序路由(以藍圖main為例)

app/main/views.py


# -*- coding:utf-8 -*-
__author__ = '東方鶚'

from flask import render_template
from . import main


@main.route('/', methods=['GET', 'POST'])
def index():

    return render_template('main/index.html')

templates/main/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>藍圖main歡迎您署驻!</h1>
</body>
</html>

在藍圖中編寫視圖函數(shù)主要有兩點不同:第一奋献,和前面的錯誤處理程序一樣,路由修飾器由藍圖提供旺上;第二瓶蚂,url_for()函數(shù)的用法不同。在藍圖中宣吱,F(xiàn)lask會為藍圖中的全部端點加上一個 ** 命名空間 ** 窃这,這樣就可以在不同的藍圖中使用相同的端點名定義視圖函數(shù),而不會產(chǎn)生沖突征候。命名空間就是藍圖的名字(Blueprint構造函數(shù)的第一個參數(shù))杭攻,所以視圖函數(shù)index()注冊的端點名是main.index,其URL使用url_for('main.index')獲取疤坝。

啟動腳本

manage.py

# -*- coding:utf-8 -*-
__author__ = '東方鶚'

import os
from app import create_app, db
from flask_script import Manager, Shell
# from flask_migrate import Migrate, MigrateCommand

app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app=app)
# migrate = Migrate(app=app, db=db)


def make_shell_context():
    return dict(app=app, db=db)

manager.add_command("shell", Shell(make_context=make_shell_context))
# manager.add_command('db', MigrateCommand)


@manager.command
def test():
    """ 單元測試 """
    import unittest
    tests = unittest.TestLoader().discover('tests')
    unittest.TextTestRunner(verbosity=2).run(test=tests)

if __name__ == '__main__':
    manager.run()

需求文件

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

(flask)$ pip freeze > requirements.txt

安裝或升級包后历谍,最好更新這個文件现拒。

如果你要創(chuàng)建這個虛擬環(huán)境的完全副本,你可以創(chuàng)建一個心的虛擬環(huán)境望侈,并在其上運行以下命令:

(flask)$ pip install -r requirements.txt

如果在今后由于升級程序包遇到問題印蔬,你可以隨時換回這個需求文件中的版本,因為這些版本和程序兼容甜无。

結果展示

啟動程序

(flask)$ python manage.py runserver --host 0.0.0.0

在瀏覽器中輸入http://localhost:5000/main扛点,顯示如下內容。
[圖片上傳失敗...(image-8ff1f3-1509964243171)]

OK岂丘,到此一個按照大型項目結構組織的項目已經(jīng)完成了基礎的構造陵究。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市奥帘,隨后出現(xiàn)的幾起案子铜邮,更是在濱河造成了極大的恐慌,老刑警劉巖寨蹋,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件松蒜,死亡現(xiàn)場離奇詭異,居然都是意外死亡已旧,警方通過查閱死者的電腦和手機秸苗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來运褪,“玉大人惊楼,你說我怎么就攤上這事〗斩铮” “怎么了檀咙?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長璃诀。 經(jīng)常有香客問我弧可,道長,這世上最難降的妖魔是什么劣欢? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任棕诵,我火速辦了婚禮,結果婚禮上氧秘,老公的妹妹穿的比我還像新娘年鸳。我一直安慰自己,他們只是感情好丸相,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布搔确。 她就那樣靜靜地躺著,像睡著了一般灭忠。 火紅的嫁衣襯著肌膚如雪膳算。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天弛作,我揣著相機與錄音涕蜂,去河邊找鬼。 笑死映琳,一個胖子當著我的面吹牛机隙,可吹牛的內容都是我干的蜘拉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼有鹿,長吁一口氣:“原來是場噩夢啊……” “哼旭旭!你這毒婦竟也來了?” 一聲冷哼從身側響起葱跋,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤持寄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后娱俺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稍味,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年荠卷,在試婚紗的時候發(fā)現(xiàn)自己被綠了模庐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡僵朗,死狀恐怖赖欣,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情验庙,我是刑警寧澤顶吮,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站粪薛,受9級特大地震影響悴了,放射性物質發(fā)生泄漏。R本人自食惡果不足惜违寿,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一湃交、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧藤巢,春花似錦搞莺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至绍刮,卻和暖如春温圆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背孩革。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工岁歉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人膝蜈。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓锅移,卻偏偏與公主長得像熔掺,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子非剃,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內容

  • 22年12月更新:個人網(wǎng)站關停瞬女,如果仍舊對舊教程有興趣參考 Github 的markdown內容[https://...
    tangyefei閱讀 35,182評論 22 257
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,138評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)努潘,斷路器,智...
    卡卡羅2017閱讀 134,657評論 18 139
  • flask是python的一個web應用框架坤学,django很多人聽過疯坤,flask比較少見,連創(chuàng)始人一開始寫出來只是...
    思而憂閱讀 2,943評論 0 5
  • 這個地方的風俗深浮,結婚當天大喇叭里放音樂压怠,搞得不得安寧。大喇叭從早上六點半一直到下午兩點飞苇,我已經(jīng)崩潰菌瘫。。布卡。還好雨让,明天...
    HR老白張小編閱讀 188評論 0 1