翻譯原文:https://lepture.com/en/2018/structure-of-a-flask-project
原文作者:lepture, pallets 小組成員,flask 系列核心開發者
flask 非常靈活,它沒有一個固定的項目目錄組織結構。這裏寫的只是我的一些建議
Flask 非常靈活,它可以讓有經驗的開發人員按照他們自己喜歡來組織項目的目錄結構。但是對於新手來說會感到困惑,他們在組織項目目錄結構時需要一些指導,並且通常情況下他們會去找一些項目結構示例,但這些示例總不是那麼好(甚至很糟糕)。
我並不知道還有這樣的問題,直到有人在 Authlib 中提 issue。剛開始我還不能理解問題,後來有人以項目結構的方式向我解釋,我終於明白了。
我嚇壞了,因爲很多文章,指南,樣板文件是從項目根 __init__.py
逆向導入模塊的:
# project/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
db.init_app(app)
# project/auth/models.py
from .. import db
class User(db.Model):
# define columns
代碼本身是可以工作的,但當你項目變大時,你將面臨一個循環依賴的問題。例如,另一個擴展需要與用戶模型一起初始化:
# project/__init__.py
from flask_sqlalchemy import SQLAlchemy
from another_extension import AnotherExtension
from project.auth.models import User
db = SQLAlchemy()
ext = AnotherExtension(User)
糟糕,發生依賴關係包循環導入問題。因爲 auth.models
正在從項目根目錄下導入 db,而根目錄下不能導入 User module。這是一個常見的循環導入問題,不只限於 flask 。這個問題很容易解決,但是初級開發人員來說可能會很難。所以爲什麼不在開始的時候就避免它呢。事實上,如果你閱讀了官方文檔,你會在應用工廠中找到這段代碼:
def create_app(config_filename):
app = Flask(__name__)
app.config.from_pyfile(config_filename)
from yourapplication.model import db
db.init_app(app)
from yourapplication.views.admin import admin
from yourapplication.views.frontend import frontend
app.register_blueprint(admin)
app.register_blueprint(frontend)
return app
這裏,我們把 db 放在 yourapplication.model
中。
我在編寫模塊和包時,總是保持這個特定的規則:
不要從根目錄下的
__init__.py
中逆向導入
這就是在我發現這個問題之後盡我可能快的提交 ticket 到 Flask 的原因。人們在項目目錄結構組織時需要一個指導。這裏我分享下我的建議,但是還是要自己思考,不要把我當作金科玉律。
基於功能的結構
設置項目目錄結構有很多種方式。一種是按它的功能來組織,例如:
project/
__init__.py
models/
__init__.py
base.py
users.py
posts.py
...
routes/
__init__.py
home.py
account.py
dashboard.py
...
templates/
base.html
post.html
...
services/
__init__.py
google.py
mail.py
...
一切都是按功能分組的。如果它的行爲像模型,則將它放在 models 目錄;如果它的行爲像路由,則將它放入 routes 目錄。在 project/__init__.py
中創建一個 create_app
工廠函數,並且初始化所有應用 init_app:
# project/__init__.py
from flask import Flask
def create_app()
from . import models, routes, services
app = Flask(__name__)
models.init_app(app)
routes.init_app(app)
services.init_app(app)
return app
這裏是我的一個小技巧。在官方文檔中,Flask-SQlAlchemy
的 db 是通過這個方式註冊的:
from project.models import db
db.init_app(app)
所是我的技巧是在每個目錄下的 __init__.py
中定義一個 init_app
函數,並且統一初始化進程:
# project/models/__init__.py
from .base import db
def init_app(app):
db.init_app(app)
# project/routes/__init__.py
from .users import user_bp
from .posts import posts_bp
# ...
def init_app(app):
app.register_blueprint(user_bp)
app.register_blueprint(posts_bp)
# ...
基於應用的結構
另一個著名的目錄結構是基於應用的結構,這意味着按照業務項目的應用程序來分組。例如:
project/
__init__.py
db.py
auth/
__init__.py
route.py
models.py
templates/
blog/
__init__.py
route.py
models.py
templates/
...
每個目錄都對應一個應用。Django 默認是使用這種方式來組織目錄。當然這並不意味該方式是很好的,你需要按照項目來選擇目錄結構。某些時候,你將不得不使用一個混合模式。
類似於上面的,你可以像這樣來 init_app :
# project/__init__.py
from flask import Flask
def create_app()
from . import db, auth, blog
app = Flask(__name__)
db.init_app(app)
auth.init_app(app)
blog.init_app(app)
return app
配置
加載配置將是另一個問題,我不知道其他人是怎麼做的,我只是分享我的解決方案。
- 在項目目錄下放一個
settings.py
文件,把它當作靜態配置。 - 從環境變量中加載配置。
- 在
create_app
中更新配置。
這是一個配置的基礎目錄結構:
conf/
dev_config.py
test_config.py
project/
__init__.py
settings.py
app.py
定義一個 create_app
來加載配置和環境變量:
# project/__init__.py
import os
from flask import Flask
def create_app(config=None)
app = Flask(__name__)
# load default configuration
app.config.from_object('project.settings')
# load environment configuration
if 'FLASK_CONF' in os.environ:
app.config.from_envvar('FLASK_CONF')
# load app sepcified configuration
if config is not None:
if isinstance(config, dict):
app.config.update(config)
elif config.endswith('.py'):
app.config.from_pyfile(config)
return app
FLASK_CONF
是一個包含配置的 python 文件(包含路徑)。這可以是任何你想要的名稱, 如項目叫做 Expanse
,你可以叫它 Expanse_CONF
。
我使用 FLASK_CONF
來加載生產環境的配置。
再一次說明,Flask 是非常靈活的,沒有固定的模式。在 Flask 中你總是可以找到你所喜歡的。以上只是我的建議方案,不要被任何人矇住眼睛。
我不喜歡寫這樣的文章。但有很多錯誤的指導,我希望這篇文章能得到更好的搜索引擎優化,這樣不好的帖子就不會再誤導人了。