目录

Flask 项目的目录结构

Flask 是一个非常灵活的 Web 开发框架,因而其目录结构可以有多种形式。

单独实例化扩展对象

在相关文件中单独实例化扩展对象,而不是将扩展对象放入__init__.py中。这是 Flask 官方文档推荐的方式

引用原作者的一句话:

Don’t backward import from root __init__.py.

示例

举个例子,我们使用 Flask-SQLAlchemy 扩展,则在 models.py 中创建 SQLAlchemy 对象。

yourapplication/__init__.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from flask import 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

yourapplication/models.py

1
2
3
4
5
6
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class User(db.Model):
    pass

原因

为了避免一些情况下的循环导入问题。

假如我们在 __init__.py 中实例化 SQLAlchemy 对象,那么在 models.py 需要从 __init__.py 中导入该对象以便创建数据库模型。但若另一扩展使用某个模型进行初始化,则需要在 __init__.py 中导入模型。这就造成了 models.py__init__.py 之间的循环导入。

yourapplication/__init__.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from yourapplication.models import User

db = SQLAlchemy()
ext = AnotherExtension(User)

def create_app(config_filename):
    app = Flask(__name__)
    app.config.from_pyfile(config_filename)

    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

yourapplication/models.py

1
2
3
4
from yourapplication import db

class User(db.Model):
    pass

基于功能的组织结构

所有内容按照其功能(模型、路由、模版、API 等等)分别存放在不同目录中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
project
├── __init__.py
├── models
│   ├── __init__.py
│   ├── posts.py
│   └── users.py
├── routes
│   ├── __init__.py
│   ├── auth.py
│   ├── dashboard.py
│   └── home.py
├── services
│   ├── __init__.py
│   ├── google.py
│   └── mail.py
└── templates
    ├── home.html
    └── login.html

在 project/__init__.py 文件中编写 create_app 工厂函数,并执行所有的 init_app 方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
## 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

这里原作者的一个小技巧是,在每个目录的 __init__.py 文件中,都定义一个 init_app 方法,在其中再调用 Flask 扩展的 init_app 方法或注册蓝本。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 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)

基于 app 的组织结构

基于 app 的内容,或者说基于蓝本来组织目录结构,每个蓝本为一个目录,每个目录下有自己单独的路由、模型、模版等。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
project
├── __init__.py
├── auth
│   ├── __init__.py
│   ├── models.py
│   ├── routes.py
│   └── templates
├── blog
│   ├── __init__.py
│   ├── models.py
│   ├── routes.py
│   └── templates
└── db.py

这是另一个比较有名的组织方式,《Flask Web 开发:基于 Python 的 Web 应用开发实战》书中即使用了该种方式,这也是 Django 的默认模式。

但有的时候,可能需要以上两种模式混合使用。例如,有的数据库模型可能在多个蓝本中都会用到,不好区分他们究竟属于哪个蓝本。

参考链接