1. 代码结构优化:StructureA
最初的 Flask 项目结构适用于小型应用,但不适用于大型应用。为了改进代码结构,我们将 URL 管理应用拆分为多个模块。
1.1 StructureA 目录结构
StructureA
|-- .flaskenv
|-- app.py
|-- views.py
|-- templates
|-- base.html
|-- home.html
|-- list.html
app.py
负责初始化 Flask 应用views.py
负责定义视图函数templates/
存放 HTML 模板
1.2 视图文件(views.py)
from flask import render_template
from app import app
@app.route("/")
def home():
return render_template('home.html', name='Alan')
@app.route("/mylist")
def my_list():
lst = ['Car', 'House', 'TV']
return render_template('list.html', lst=lst)
app
变量需要在views.py
导入前初始化,否则会导致app
未定义的错误。
1.3 基础模板(base.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>StructureA</title>
</head>
<body>
<div>
<a href="{{ url_for('home') }}">Home</a>
<a href="{{ url_for('my_list') }}">List</a>
</div>
{% block body %}
{% endblock %}
</body>
</html>
- 使用
url_for()
生成导航链接,使代码更具可维护性。
1.4 主页模板(home.html)
{% extends "base.html" %}
{% block body %}
<h1>Hello {{ name }}</h1>
{% endblock %}
1.5 列表页模板(list.html)
{% extends "base.html" %}
{% block body %}
<h1>My List</h1>
<ul>
{% for item in lst %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endblock %}
1.6 应用入口(app.py)
from flask import Flask
from jinja2 import StrictUndefined
app = Flask(__name__)
app.jinja_env.undefined = StrictUndefined
import views
- Flask
app
需要在views.py
之前初始化。 - 这样组织代码会导致导入复杂化,并可能出现 循环导入 问题,因此需要更好的结构。
2. Python 模块与导入机制
Python 文件可以作为模块导入其他 Python 文件。模块是包含变量、函数或类定义的文件,每个模块都有自己的命名空间。
2.1 模块的导入方式
import x # 导入模块 x,使用 x.y 访问其内部成员
from x import y, z # 直接导入 y, z,不需要加 x.
-
模块首次导入时,Python 会执行该文件中的所有语句,这可能导致意外的副作用。
-
循环导入 是大型项目中的常见问题,例如:
# a.py import b # 这里导入了 b.py # b.py import a # 这里导入了 a.py
- Python 发现
a
还没有完全加载,会导致b
不能正确导入a
中的对象,从而引发错误。
- Python 发现
3. 代码结构优化:StructureB
为了更好的管理项目,我们采用 包(Package) 来组织代码。
3.1 StructureB 目录结构
StructureB
|-- .flaskenv
|-- run.py
|-- app
|-- __init__.py
|-- views.py
|-- templates
|-- base.html
|-- home.html
|-- list.html
app/
变成了一个 Python 包 ,其中包含__init__.py
作为包的初始化文件。run.py
作为应用的入口点。
3.2 应用入口(run.py)
from app import app
run.py
仅用于导入app
,然后 Flask 运行app
作为应用实例。
3.3 应用初始化(init.py)
from flask import Flask
from jinja2 import StrictUndefined
app = Flask(__name__)
app.jinja_env.undefined = StrictUndefined
from app import views
- 这里的
app
在__init__.py
中定义,使得整个app/
目录成为一个包。 - 好处 :
- 允许在
app/
目录中添加多个模块,而不会导致导入冲突。 - 避免
app.py
直接执行时的循环导入问题。
- 允许在
4. Flask 中的静态文件与数据文件
4.1 静态文件(static/)
Flask 默认会寻找 static/
目录来提供静态资源(如 CSS、JS、图片等)。
-
访问静态文件:
<img src="{{ url_for('static', filename='images/pic.jpg') }}">
-
url_for('static', filename='...')
使得路径动态生成,更易维护。
4.2 数据文件(data/)
Flask 没有 data/
目录的特殊约定,但它通常用于存储不可通过 URL 访问的文件(如数据库、文本文件等)。
-
推荐的访问方式:
with app.open_resource('data/quotes.txt') as file: app.globals_quotes = [line.strip() for line in file]
-
存入 Flask 全局对象 :
app.globals_quotes = some_data
5. 总结
改进点 | StructureA | StructureB |
---|---|---|
代码组织 | 扁平结构,所有代码在 app.py 中 |
采用包结构,app/ 作为 Flask 应用 |
视图管理 | 直接在 app.py 中定义 |
views.py 独立存放 |
启动方式 | python app.py |
python run.py |
代码可维护性 | 易出现循环导入问题 | 结构清晰,模块化管理 |
6. Flask 项目最佳实践
- 使用包结构 (
app/
+__init__.py
),避免循环导入问题。 - 将视图拆分为模块 ,避免
app.py
过大。 - 使用
static/
存放静态文件 ,并通过url_for()
生成链接。 - 使用
data/
存放非 URL 访问的数据 ,并通过app.open_resource()
读取。