【百度云搜索,搜各種資料:http://bdy.lqkweb.com】
【搜網(wǎng)盤蛉艾,搜各種資料:http://www.swpan.cn】
本文翻譯自The Flask Mega-Tutorial Part XV: A Better Application Structure
這是Flask Mega-Tutorial系列的第十五部分黄橘,我將使用適用于大型應用的風格重構本應用碱鳞。
Microblog已經(jīng)是一個初具規(guī)模的應用了勃黍,所以我認為這是討論Flask應用如何在持續(xù)增長中不會變得混亂和難以管理的好時機块请。 Flask是一個框架聋涨,旨在讓你選擇以任何方式來組織項目,基于該理念负乡,在應用日益龐大或者技能水平變化的時候牍白,才有可能更改和調(diào)整其結構。
在本章中抖棘,我將討論適用于大型應用的一些模式茂腥,并且為了演示他們,我將對Microblog項目的結構進行一些更改切省,目標是使代碼更易于維護和組織最岗。 當然,在真正的Flask精神中朝捆,我鼓勵你在嘗試決定組織自己的項目的方式時僅僅將這些更改作為參考般渡。
本章的GitHub鏈接為:Browse, Zip, Diff.
目前的局限性
目前狀態(tài)下的應用有兩個基本問題。 如果你觀察應用的組織方式芙盘,你會注意到有幾個不同的子系統(tǒng)可以被識別驯用,但支持它們的代碼都混合在了一起,沒有任何明確的界限儒老。 我們來回顧一下這些子系統(tǒng)是什么:
- 用戶認證子系統(tǒng)蝴乔,包括app/routes.py中的一些視圖函數(shù),app/forms.py中的一些表單驮樊,app/templates中的一些模板以及*app/email.py中的電子郵件支持薇正。
- 錯誤子系統(tǒng),它在app/errors.py中定義了錯誤處理程序并在app/templates中定義了模板囚衔。
- 核心應用功能挖腰,包括顯示和撰寫用戶動態(tài),用戶個人主頁和關注以及用戶動態(tài)的實時翻譯练湿,這些功能遍布大多數(shù)應用模塊和模板猴仑。
思考這三個子系統(tǒng)以及它們組織的方式,你可能會注意到這樣一個模式鞠鲜。 到目前為止宁脊,我一直遵循的組織邏輯是不同的應用功能歸屬到其專屬的模塊。 這些模塊之中贤姆,一個用于視圖函數(shù)榆苞,一個用于Web表單,一個用于錯誤霞捡,一個用于電子郵件坐漏,一個目錄用于存放HTML模板等等。 雖然這是一個對小項目有意義的組織結構,但是一旦項目開始增長赊琳,它往往會使其中的一些模塊變得非常大而且雜亂無章街夭。
要想清晰地看到問題的一種方法,是思考如何通過盡可能多地重復使用這一項目來開始第二個項目。 例如,用戶身份驗證部分應該在其他應用中也能運行良好适袜,但如果你想按原樣使用該代碼,則必須進入多個模塊并將相關部分復制/粘貼到新項目的新文件中埃碱。 看到這是多么不方便了嗎? 如果這個項目將所有與認證相關的文件從應用的其余部分中分離出來酥泞,會不會更好砚殿? Flask的blueprints功能有助于實現(xiàn)更實用的組織結構,從而更輕松地重用代碼芝囤。
還有第二個問題似炎,雖然它不太明顯。 Flask應用實例在app/__init__.py
中被創(chuàng)建為一個全局變量悯姊,然后又被很多應用模塊導入羡藐。 雖然這本身并不是問題,但將應用實例作為全局變量可能會使某些情況復雜化挠轴,特別是與測試相關的情景传睹。 想象一下你想要在不同的配置下測試這個應用耳幢。 由于應用被定義為全局變量岸晦,實際上沒有辦法使用不同配置變量來實例化的兩個應用實例。 另一種糟心的情況是睛藻,所有測試都使用相同的應用启上,因此測試可能會對應用進行更改,就會影響稍后運行的其他測試店印。 理想情況下冈在,你希望所有測試都在原始應用實例上運行的。
你可以在tests.py模塊中看到我正在使用的應用實例化之后修改配置的技巧按摘,以指示測試時使用內(nèi)存數(shù)據(jù)庫而不是默認的SQLite數(shù)據(jù)庫包券。我真的沒有其他辦法來更改已配置的數(shù)據(jù)庫,因為在測試開始時已經(jīng)創(chuàng)建和配置了應用炫贤。 對于這種特殊情況溅固,對已配置的應用實例修改配置似乎可以運行,但在其他情況下可能不會兰珍,并且在任何情況下侍郭,這是一種不推薦的做法,因為這么做可能會導致提示晦澀并且難以找到BUG。
更好的解決方案是不將應用設置為全局變量亮元,而是使用應用工廠函數(shù)在運行時創(chuàng)建它猛计。 這將是一個接受配置對象作為參數(shù)的函數(shù),并返回一個配置完畢的Flask應用實例爆捞。 如果我能夠通過應用工廠函數(shù)來修改應用奉瘤,那么編寫需要特殊配置的測試會變得很容易,因為每個測試都可以創(chuàng)建它各自的應用煮甥。
在本章中毛好,我將通過為上面提到的三個子系統(tǒng)重構應用來介紹blueprints。 展示更改的詳細列表有些不切實際苛秕,因為幾乎應用中每個文件都有少許變化肌访,所以我將討論重構的步驟,然后你可以下載更改后的應用艇劫。
Blueprints
在Flask中吼驶,blueprint是代表應用子集的邏輯結構。 blueprint可以包括路由店煞,視圖函數(shù)蟹演,表單,模板和靜態(tài)文件等元素顷蟀。 如果在單獨的Python包中編寫blueprint酒请,那么你將擁有一個封裝了應用特定功能的組件。
Blueprint的內(nèi)容最初處于休眠狀態(tài)鸣个。 為了關聯(lián)這些元素羞反,blueprint需要在應用中注冊。 在注冊過程中囤萤,需要將添加到blueprint中的所有元素傳遞給應用昼窗。 因此,你可以將blueprint視為應用功能的臨時存儲涛舍,以幫助組織代碼澄惊。
錯誤處理Blueprint
我創(chuàng)建的第一個blueprint用于封裝對錯誤處理程序的支持。 該blueprint的結構如下:
app/
errors/ <-- blueprint package
__init__.py <-- blueprint creation
handlers.py <-- error handlers
templates/
errors/ <-- error templates
404.html
500.html
__init__.py <-- blueprint registration
實質(zhì)上富雅,我所做的是將app/errors.py模塊移動到app/errors/handlers.py中掸驱,并將兩個錯誤模板移動到app/templates/errors中,以便將它們與其他模板分開没佑。 我還必須在兩個錯誤處理程序中更改render_template()
調(diào)用以使用新的errors模板子目錄毕贼。 之后,我將blueprint創(chuàng)建添加到app/errors/init.py模塊图筹,并在創(chuàng)建應用實例之后帅刀,將blueprint注冊到app/init.py让腹。
我必須提一下,F(xiàn)lask blueprints可以為自己的模板和靜態(tài)文件配置單獨的目錄扣溺。 我已決定將模板移動到應用模板目錄的子目錄中骇窍,以便所有模板都位于一個層次結構中,但是如果你希望在blueprint中包含屬于自己的模板锥余,這也是支持的腹纳。 例如,如果向Blueprint()
構造函數(shù)添加template_folder='templates'
參數(shù)驱犹,則可以將錯誤blueprint的模板存儲在app/errors/templates目錄中嘲恍。
創(chuàng)建blueprint與創(chuàng)建應用非常相似。 這是在blueprint的___init__.py
模塊中完成的:
app/errors/__init__.py
:錯誤blueprint雄驹。
from flask import Blueprint
bp = Blueprint('errors', __name__)
from app.errors import handlers
Blueprint
類獲取blueprint的名稱佃牛,基礎模塊的名稱(通常在Flask應用實例中設置為__name__
)以及一些可選參數(shù)(在這種情況下我不需要這些參數(shù))。 Blueprint對象創(chuàng)建后医舆,我導入了handlers.py模塊俘侠,以便其中的錯誤處理程序在blueprint中注冊。 該導入位于底部以避免循環(huán)依賴蔬将。
在handlers.py模塊中爷速,我放棄使用@app.errorhandler
裝飾器將錯誤處理程序附加到應用程序,而是使用blueprint的@bp.app_errorhandler
裝飾器霞怀。 盡管兩個裝飾器最終都達到了相同的結果惫东,但這樣做的目的是試圖使blueprint獨立于應用,使其更具可移植性毙石。我還需要修改兩個錯誤模板的路徑廉沮,因為它們被移動到了新errors子目錄。
完成錯誤處理程序重構的最后一步是向應用注冊blueprint:
app/init.py:向應用注冊錯誤blueprint胁黑。
app = Flask(__name__)
# ...
from app.errors import bp as errors_bp
app.register_blueprint(errors_bp)
# ...
from app import routes, models # <-- remove errors from this import!
為了注冊blueprint废封,將使用Flask應用實例的register_blueprint()
方法。 在注冊blueprint時丧蘸,任何視圖函數(shù),模板遥皂,靜態(tài)文件力喷,錯誤處理程序等均連接到應用。 我將blueprint的導入放在app.register_blueprint()
的上方演训,以避免循環(huán)依賴弟孟。
用戶認證Blueprint
將應用的認證功能重構為blueprint的過程與錯誤處理程序的過程非常相似。 以下是重構為blueprint的目錄層次結構:
app/
auth/ <-- blueprint package
__init__.py <-- blueprint creation
email.py <-- authentication emails
forms.py <-- authentication forms
routes.py <-- authentication routes
templates/
auth/ <-- blueprint templates
login.html
register.html
reset_password_request.html
reset_password.html
__init__.py <-- blueprint registration
為了創(chuàng)建這個blueprint样悟,我必須將所有認證相關的功能移到為blueprint創(chuàng)建的新模塊中拂募。 這包括一些視圖函數(shù)庭猩,Web表單和支持功能,例如通過電子郵件發(fā)送密碼重設token的功能陈症。 我還將模板移動到一個子目錄中蔼水,以將它們與應用的其余部分分開,就像我對錯誤頁面所做的那樣录肯。
在blueprint中定義路由時趴腋,使用@bp.route
裝飾器來代替@app.route
裝飾器。 在url_for()
中用于構建URL的語法也需要進行更改论咏。 對于直接附加到應用的常規(guī)視圖函數(shù)优炬,url_for()
的第一個參數(shù)是視圖函數(shù)名稱。 但當在blueprint中定義路由時厅贪,該參數(shù)必須包含blueprint名稱和視圖函數(shù)名稱蠢护,并以句點分隔。 因此养涮,我不得不用諸如url_for('auth.login')
的代碼替換所有出現(xiàn)的url_for('login')
代碼糊余,對于其余的視圖函數(shù)也是如此。
注冊auth
blueprint到應用時单寂,我使用了些許不同的格式:
app/__init__.py
:注冊用戶認證blueprint到應用贬芥。
# ...
from app.auth import bp as auth_bp
app.register_blueprint(auth_bp, url_prefix='/auth')
# ...
在這種情況下,register_blueprint()
調(diào)用接收了一個額外的參數(shù)宣决,url_prefix
蘸劈。 這完全是可選的,F(xiàn)lask提供了給blueprint的路由添加URL前綴的選項尊沸,因此blueprint中定義的任何路由都會在其完整URL中獲取此前綴威沫。 在許多情況下,這可以用來當成“命名空間”洼专,它可以將blueprint中的所有路由與應用或其他blueprint中的其他路由分開棒掠。 對于用戶認證,我認為讓所有路由以/auth開頭很不錯屁商,所以我添加了該前綴烟很。 所以現(xiàn)在登錄URL將會是http://localhost:5000/auth/login。 因為我使用url_for()
來生成URL蜡镶,所有URL都會自動合并前綴雾袱。
主應用Blueprint
第三個blueprint包含核心應用邏輯。 重構這個blueprint和前兩個blueprint的過程一樣官还。 我給這個blueprint命名為main
芹橡,因此所有引用視圖函數(shù)的url_for()
調(diào)用都必須添加一個main.
前綴。 鑒于這是應用的核心功能望伦,我決定將模板留在原來的位置林说。 這不會有什么問題煎殷,因為我已將其他兩個blueprint中的模板移動到子目錄中了。
應用工廠模式
正如我在本章的介紹中所提到的腿箩,將應用設置為全局變量會引入一些復雜性豪直,主要是以某些測試場景的局限性為形式。 在我介紹blueprint之前度秘,應用必須是一個全局變量顶伞,因為所有的視圖函數(shù)和錯誤處理程序都需要使用來自app
的裝飾器來修飾,比如@app.route
剑梳。 但是現(xiàn)在所有的路由和錯誤處理程序都被轉移到了blueprint中唆貌,因此保持應用全局性的理由就不夠充分了。
所以我要做的是添加一個名為create_app()
的函數(shù)來構造一個Flask應用實例垢乙,并消除全局變量锨咙。 轉換并非容易,我不得不理清一些復雜的東西追逮,但我們先來看看應用工廠函數(shù):
app/__init__.py
:應用工廠函數(shù)酪刀。
# ...
db = SQLAlchemy()
migrate = Migrate()
login = LoginManager()
login.login_view = 'auth.login'
login.login_message = _l('Please log in to access this page.')
mail = Mail()
bootstrap = Bootstrap()
moment = Moment()
babel = Babel()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
migrate.init_app(app, db)
login.init_app(app)
mail.init_app(app)
bootstrap.init_app(app)
moment.init_app(app)
babel.init_app(app)
# ... no changes to blueprint registration
if not app.debug and not app.testing:
# ... no changes to logging setup
return app
你已經(jīng)看到,大多數(shù)Flask插件都是通過創(chuàng)建插件實例并將應用作為參數(shù)傳遞來初始化的钮孵。 當應用不再作為全局變量時骂倘,有一種替代模式,插件分成兩個階段進行初始化巴席。 插件實例首先像前面一樣在全局范圍內(nèi)創(chuàng)建历涝,但沒有參數(shù)傳遞給它。 這會創(chuàng)建一個未附加到應用的插件實例漾唉。 當應用實例在工廠函數(shù)中創(chuàng)建時荧库,必須在插件實例上調(diào)用init_app()
方法,以將其綁定到現(xiàn)在已知的應用赵刑。
在初始化期間執(zhí)行的其他任務保持不變分衫,但會被移到工廠函數(shù)而不是在全局范圍內(nèi)。 這包括blueprint和日志配置的注冊般此。 請注意蚪战,我在條件中添加了一個not app.testing
子句,用于決定是否啟用電子郵件和文件日志恤煞,以便在單元測試期間跳過所有這些日志記錄屎勘。 由于在配置中TESTING
變量在單元測試時會被設置為True
,因此app.testing
標志在運行單元測試時將變?yōu)?code>True居扒。
那么誰來調(diào)用應用程工廠函數(shù)呢? 最明顯使用此函數(shù)的地方是處于頂級目錄的microblog.py腳本丑慎,它是唯一會將應用設置為全局變量的模塊喜喂。 另一個調(diào)用該工廠函數(shù)的地方是tests.py瓤摧,我將在下一節(jié)中更詳細地討論單元測試。
正如我上面提到的玉吁,大多數(shù)對app
的引用都是隨著blueprint的引入而消失的照弥,但是我仍然需要解決代碼中的一些問題。 例如进副,app/models.py这揣、app/translate.py和app/main/routes.py模塊都引用了app.config
。 幸運的是影斑,F(xiàn)lask開發(fā)人員試圖使視圖函數(shù)很容易地訪問應用實例给赞,而不必像我一直在做的那樣導入它。 Flask提供的current_app
變量是一個特殊的“上下文”變量矫户,F(xiàn)lask在分派請求之前使用應用初始化該變量片迅。 你之前已經(jīng)看到另一個上下文變量,即存儲當前語言環(huán)境的g
變量皆辽。 這兩個變量柑蛇,以及Flask-Login的current_user
和其他一些你還沒有看到的東西,是“魔法”變量驱闷,因為它們像全局變量一樣工作耻台,但只能在處理請求期間且在處理它的線程中訪問。
用Flask的current_app
變量替換app
就不需要將應用實例作為全局變量導入空另。 通過簡單的搜索和替換盆耽,我可以毫無困難地用current_app.config
替換對app.config
的所有引用。
app/email.py模塊提出了一個更大的挑戰(zhàn)痹换,所以我必須使用一個小技巧:
app/email.py:將應用實例傳遞給另一個線程征字。
from app import current_app
def send_async_email(app, msg):
with app.app_context():
mail.send(msg)
def send_email(subject, sender, recipients, text_body, html_body):
msg = Message(subject, sender=sender, recipients=recipients)
msg.body = text_body
msg.html = html_body
Thread(target=send_async_email,
args=(current_app._get_current_object(), msg)).start()
在send_email()
函數(shù)中,應用實例作為參數(shù)傳遞給后臺線程娇豫,后臺線程將發(fā)送電子郵件而不阻塞主應用程序匙姜。在作為后臺線程運行的send_async_email()
函數(shù)中直接使用current_app
將不會奏效,因為current_app
是一個與處理客戶端請求的線程綁定的上下文感知變量冯痢。在另一個線程中氮昧,current_app
沒有賦值。直接將current_app
作為參數(shù)傳遞給線程對象也不會有效浦楣,因為current_app
實際上是一個代理對象袖肥,它被動態(tài)地映射到應用實例。因此振劳,傳遞代理對象與直接在線程中使用current_app
相同椎组。我需要做的是訪問存儲在代理對象中的實際應用程序?qū)嵗⑵渥鳛?code>app參數(shù)傳遞历恐。 current_app._get_current_object()
表達式從代理對象中提取實際的應用實例寸癌,所以它就是我作為參數(shù)傳遞給線程的专筷。
另一個棘手的模塊是app/cli.py,它實現(xiàn)了一些用于管理語言翻譯的快捷命令蒸苇。 在這種情況下磷蛹,current_app
變量不起作用,因為這些命令是在啟動時注冊的溪烤,而不是在處理請求期間(這是唯一可以使用current_app
的時間段)注冊的味咳。 為了在這個模塊中刪除對app
的引用,我使用了另一個技巧檬嘀,將這些自定義命令移動到一個將app
實例作為參數(shù)的register()
函數(shù)中:
app/cli.py:注冊自定義應用命令槽驶。
import os
import click
def register(app):
@app.cli.group()
def translate():
"""Translation and localization commands."""
pass
@translate.command()
@click.argument('lang')
def init(lang):
"""Initialize a new language."""
# ...
@translate.command()
def update():
"""Update all languages."""
# ...
@translate.command()
def compile():
"""Compile all languages."""
# ...
然后我從microblog.py中調(diào)用這個register()
函數(shù)。 以下是完成重構后的microblog.py:
microblog.py:重構后的主應用模塊枪眉。
from app import create_app, db, cli
from app.models import User, Post
app = create_app()
cli.register(app)
@app.shell_context_processor
def make_shell_context():
return {'db': db, 'User': User, 'Post' :Post}
單元測試的改進
正如我在本章開頭所暗示的捺檬,到目前為止,我所做的很多工作都是為了改進單元測試工作流程贸铜。 在運行單元測試時堡纬,要確保應用的配置方式不會污染開發(fā)資源(如數(shù)據(jù)庫)。
tests.py的當前版本采用了應用實例化之后修改配置的技巧蒿秦,這是一種危險的做法烤镐,因為并不是所有類型的更改都會在修改之后才生效。 我想要的是有機會在添加到應用之前指定我想要的測試配置項棍鳖。
create_app()
函數(shù)現(xiàn)在接受一個配置類作為參數(shù)炮叶。 默認情況下,使用在config.py中定義的Config
類渡处,但現(xiàn)在我可以通過將新類傳遞給工廠函數(shù)來創(chuàng)建使用不同配置的應用實例镜悉。 下面是一個適用于我的單元測試的示例配置類:
tests.py:測試配置。
from config import Config
class TestConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite://'
我在這里做的是創(chuàng)建應用的Config
類的子類医瘫,并覆蓋SQLAlchemy配置以使用內(nèi)存SQLite數(shù)據(jù)庫侣肄。 我還添加了一個TESTING
屬性,并設置為True
醇份,我目前不需要該屬性稼锅,但如果應用需要確定它是否在單元測試下運行,它就派上用場了僚纷。
你一定還記得矩距,我的單元測試依賴于setUp()
和tearDown()
方法,它們由單元測試框架自動調(diào)用怖竭,以創(chuàng)建和銷毀每次測試運行的環(huán)境锥债。 我現(xiàn)在可以使用這兩種方法為每個測試創(chuàng)建和銷毀一個測試專用的應用:
tests.py:為每次測試創(chuàng)建一個應用。
class UserModelCase(unittest.TestCase):
def setUp(self):
self.app = create_app(TestConfig)
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()
新的應用將存儲在self.app
中,但光是創(chuàng)建一個應用不足以使所有的工作都成功赞弥。 思考創(chuàng)建數(shù)據(jù)庫表的db.create_all()
語句毅整。 db
實例需要注冊到應用實例趣兄,因為它需要從app.config
獲取數(shù)據(jù)庫URI绽左,但是當你使用應用工廠時,應用就不止一個了艇潭。 那么db
如何關聯(lián)到我剛剛創(chuàng)建的self.app
實例呢拼窥?
答案在application context中。 還記得current_app
變量嗎蹋凝?當不存在全局應用實例導入時鲁纠,該變量以代理的形式來引用應用實例。 這個變量在當前線程中查找活躍的應用上下文鳍寂,如果找到了改含,它會從中獲取應用實例。 如果沒有上下文迄汛,那么就沒有辦法知道哪個應用實例處于活躍狀態(tài)捍壤,所以current_app
就會引發(fā)一個異常。 下面你可以看到它是如何在Python控制臺中工作的鞍爱。 這需要通過運行python
啟動鹃觉,因為flask shell
命令會自動激活應用程序上下文以方便使用。
>>> from flask import current_app
>>> current_app.config['SQLALCHEMY_DATABASE_URI']
Traceback (most recent call last):
...
RuntimeError: Working outside of application context.
>>> from app import create_app
>>> app = create_app()
>>> app.app_context().push()
>>> current_app.config['SQLALCHEMY_DATABASE_URI']
'sqlite:////home/miguel/microblog/app.db'
這就是秘密所在睹逃! 在調(diào)用你的視圖函數(shù)之前盗扇,F(xiàn)lask推送一個應用上下文,它會使current_app
和g
生效沉填。 當請求完成時疗隶,上下文將與這些變量一起被刪除。 為了使db.create_all()
調(diào)用在單元測試setUp()
方法中工作翼闹,我為剛剛創(chuàng)建的應用程序?qū)嵗扑土艘粋€應用上下文斑鼻,這樣db.create_all()
可以使用 current_app.config
知道數(shù)據(jù)庫在哪里。 然后在tearDown()
方法中橄碾,我彈出上下文以將所有內(nèi)容重置為干凈狀態(tài)卵沉。
你還應該知道,應用上下文是Flask使用的兩種上下文之一法牲,還有一個請求上下文史汗,它更具體,因為它適用于請求拒垃。 在處理請求之前激活請求上下文時停撞,F(xiàn)lask的request
、session
以及Flask-Login的current_user
變量才會變成可用狀態(tài)。
環(huán)境變量
正如構建此應用時你所看到的戈毒,在啟動服務器之前艰猬,有許多配置選項取決于在環(huán)境中設置的變量。 這包括密鑰埋市、電子郵件服務器信息冠桃、數(shù)據(jù)庫URL和Microsoft Translator API key。 你可能會和我一樣覺得道宅,這很不方便食听,因為每次打開新的終端會話時,都需要重新設置這些變量污茵。
譯者注:可以通過將環(huán)境變量設置到開機啟動中樱报,來保持它們在該計算機中的所有終端中都生效。
應用依賴大量環(huán)境變量的常見處理模式是將這些變量存儲在應用根目錄中的.env文件中泞当。 應用在啟動時會從此文件中導入變量迹蛤,這樣就不需要你手動設置這些變量了。
有一個支持.env文件的Python包襟士,名為python-dotenv
盗飒。 所以讓我們安裝這個包:
(venv) $ pip install python-dotenv
由于config.py模塊是我讀取所有環(huán)境變量的地方,因此我將在創(chuàng)建Config類之前導入.env文件敌蜂,以便在構造類時設置變量:
config.py:導入.env文件中的環(huán)境變量箩兽。
import os
from dotenv import load_dotenv
basedir = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(basedir, '.env'))
class Config(object):
# ...
現(xiàn)在你可以創(chuàng)建一個.env文件并在其中寫入應用所需的所有環(huán)境變量了。不要將.env文件加入到源代碼版本控制中章喉,這非常重要汗贫。否則,一旦你的密碼和其他重要信息上傳到遠程代碼庫中后秸脱,你就會后悔莫及落包。
.env文件可以用于所有配置變量,但是不能用于Flask命令行的FLASK_APP
和FLASK_DEBUG
環(huán)境變量摊唇,因為它們在應用啟動的早期(應用實例和配置對象存在之前)就被使用了咐蝇。
以下示例顯示了.env文件,該文件定義了一個安全密鑰巷查,將電子郵件配置為在本地運行的郵件服務器的25端口上有序,并且不進行身份驗證,設置Microsoft Translator API key岛请,使用數(shù)據(jù)庫配置的默認值:
SECRET_KEY=a-really-long-and-unique-key-that-nobody-knows
MAIL_SERVER=localhost
MAIL_PORT=25
MS_TRANSLATOR_KEY=<your-translator-key-here>
依賴文件
此時我已經(jīng)在Python虛擬環(huán)境中安裝了一定數(shù)量的軟件包旭寿。 如果你需要在另一臺機器上重新生成你的環(huán)境,將無法記住你必須安裝哪些軟件包崇败,所以一般公認的做法是在項目的根目錄中寫一個requirements.txt文件盅称,列出所有依賴的包及其版本肩祥。 生成這個列表實際上很簡單:
(venv) $ pip freeze > requirements.txt
pip freeze
命令將安裝在虛擬環(huán)境中的所有軟件包以正確的格式輸入到requirements.txt文件中。 現(xiàn)在缩膝,如果你需要在另一臺計算機上創(chuàng)建相同的虛擬環(huán)境混狠,無需逐個安裝軟件包,可以直接運行一條命令實現(xiàn):
(venv) $ pip install -r requirements.txt