开源项目:explore-flask/README.rst at master · rpicard/explore-flask (github.com)
一、Coding conventions
Summary
Try to follow the coding style conventions laid out in PEP 8.
Try to document your app with docstrings as defined in PEP 257.
def launch_rocket():
"""Main launch sequence director.
Locks seatbelts, initiates radio and fires engines.
"""
# [...]
Use relative imports to import your app’s internal modules.
# myapp/views.py
# An absolute import gives us the User model
from myapp.models import User
# A relative import does the same thing
from .models import User
✅ 至今仍然适用的原则
1. 遵循 PEP 8 代码风格
为什么有效:
PEP 8 仍是 Python 官方推荐的代码风格标准,确保代码一致性和可读性。例如:缩进用 4 空格、变量名用
snake_case
、类名用CamelCase
。
现代工具支持:
自动格式化工具(如
black
、autopep8
)可强制遵循 PEP 8。IDE(如 VS Code、PyCharm)内置 PEP 8 检查。
注意:
PEP 8 是指南而非铁律,部分规则可灵活调整(如行长度默认 88/79 字符,但black
强制 88)。
2. 使用 PEP 257 文档字符串(Docstrings)
为什么有效:
文档字符串是 Python 生态的通用约定,IDE 和工具(如 Sphinx、pydoc)依赖它生成文档。
类型注解(Type Hints)的普及(PEP 484)并未取代文档字符串,而是互补。
推荐格式:
Google 风格(简洁)或 NumPy 风格(详细),而非严格的 PEP 257 原始格式。
def calculate(a: int, b: int) -> int:
"""Compute the sum of two integers.
Args:
a: First integer.
b: Second integer.
Returns:
Sum of a and b.
"""
return a + b
3. 相对导入(Relative Imports)的合理使用
适用场景:
在 包内部模块互相引用 时,相对导入(
from . import module
)仍是最清晰的方式。避免硬编码包名,提高可移植性(如重构时包名变更不影响导入)。
现代补充:
结合
__init__.py
和pyproject.toml
定义包结构(PEP 621)。
⚠️ 需要调整或谨慎使用的原则
1. 相对导入的潜在问题
问题场景:
在脚本直接运行(
python script.py
)时,相对导入可能失败(因__package__
未定义)。复杂的项目结构(如嵌套包)可能导致导入混乱。
现代建议:
优先使用 绝对导入(
from mypackage import module
)除非明确需要相对导入。将可执行脚本放在包外,或通过
python -m mypackage.module
运行。
2. PEP 8 的局部调整
灵活性增强:
行长度:
black
等工具默认允许 88 字符(原 PEP 8 建议 79)。类型注解:PEP 8 已更新允许更灵活的类型注解换行(PEP 484+)。
例外情况:
某些 API 设计(如 Django 的
models.ForeignKey
)可能不符合 PEP 8 命名,但遵循框架惯例更重要。
3. 文档字符串的过度工程化
旧问题:
PEP 257 的原始规范(如单行文档字符串的格式)可能过于严格。现代实践:
更注重 实用性(如 Google/NumPy 风格),而非机械遵循 PEP 257。
工具(如
pydocstyle
)可配置检查规则。
🔧 2023 年推荐的最佳实践
原则 | 现代调整建议 |
---|---|
PEP 8 风格 | 用 black 自动格式化,局部例外可通过 # fmt: off 忽略。 |
文档字符串 | 结合类型注解 + Google/NumPy 风格,用 mkdocs 或 Sphinx 生成文档。 |
相对导入 | 仅在包内部使用;脚本和顶层模块用绝对导入。 |
总结:如何应用这些原则 today?
坚持核心规范:
PEP 8 和文档字符串仍是 Python 开发的基石,但可通过工具自动化。
灵活调整细节:
行长度、导入方式等根据项目和团队需求调整。
结合现代工具链:
格式化:
black
+isort
。文档:
mkdocs-material
+pydocstyle
。导入:优先绝对导入,包内用相对导入。
这些原则的核心理念(可读性、一致性、可维护性)始终重要,但实现方式更智能高效了。
二、Environment
Summary
Use virtualenv to keep your application’s dependencies together.
Use virtualenvwrapper to keep your virtual environments together.
Keep track of dependencies with one or more text files.
Use a version control system. I recommend Git.
Use .gitignore to keep clutter and secrets out of version control.
Debug mode can give you information about problems in development.
The Flask-DebugToolbar extension will give you even more of that information.
✅ 至今仍然适用的原则
1. 使用虚拟环境隔离依赖(virtualenv)
为什么有效:
Python 项目依赖冲突问题依然存在(尤其是不同项目需要同一库的不同版本时)。虚拟环境仍是官方推荐的依赖隔离方案。现代改进:
Python 3.3+ 内置了venv
模块(python -m venv venv
),但virtualenv
仍更灵活(如支持旧版 Python)。
2. 使用版本控制系统(Git)
为什么有效:
Git 已成为行业标准(尤其是配合 GitHub/GitLab),代码版本管理、协作、回滚等需求不变。现代补充:
可结合pre-commit
等工具自动化代码检查。
3. 用 .gitignore
排除无关文件
为什么有效:
避免提交编译文件(如.pyc
)、敏感信息(如.env
)、IDE 配置等仍是基本规范。现代扩展:
现在更推荐使用 环境变量(如python-dotenv
)或 专用配置管理工具(如 Vault)管理密钥,而非手动忽略文件。
4. 记录依赖清单(requirements.txt)
为什么有效:
明确依赖是项目可复现的基础。现代改进:
推荐使用
pip-tools
或poetry
管理依赖(自动处理子依赖版本冲突)。区分开发/生产依赖(如
requirements-dev.txt
)。
5. 调试模式与工具(Flask-DebugToolbar)
为什么有效:
Debug 模式和 DebugToolbar 仍是快速定位问题的有效工具。注意点:
需确保仅限开发环境使用(生产环境禁用!)。
⚠️ 需要调整或过时的原则
1. virtualenvwrapper 的必要性降低
原因:
现代工具(如
poetry
、pipenv
)已内置虚拟环境管理功能。IDE(如 VS Code、PyCharm)直接支持虚拟环境切换,减少手动操作需求。
建议:
新项目可优先尝试poetry
(依赖管理 + 虚拟环境一体化)。
2. 纯手动维护依赖文件(pip freeze)
问题:
pip freeze
会导出所有依赖(包括间接依赖),导致文件臃肿且难以维护。替代方案:
使用
poetry
的pyproject.toml
或pipenv
的Pipfile
显式声明依赖。仅锁定版本时生成
requirements.txt
(如部署用)。
3. Flask-DebugToolbar 的局限性
现代挑战:
对异步框架(如 FastAPI)支持有限。
前端复杂应用可能需要更专业的调试工具(如浏览器 DevTools + 后端日志聚合)。
替代方案:
结合logging
、Sentry
(错误追踪)、Postman
(API 调试)等。
🔧 2023 年推荐的工具链升级
传统方式 | 现代替代方案 | 优势 |
---|---|---|
virtualenv + pip |
poetry / pdm |
依赖解析、虚拟环境管理一体化 |
requirements.txt |
pyproject.toml (PEP 621) |
标准化依赖声明,支持元数据 |
Flask-DebugToolbar |
pdbpp + logging + Sentry |
更灵活的调试和错误监控 |
总结:如何应用这些原则 today?
仍要坚持:
隔离环境、版本控制、依赖记录、调试安全。
需要更新:
用
poetry
替代virtualenvwrapper
+ 手动pip
。敏感信息改用环境变量或专用服务管理。
扩展实践:
容器化(Docker)进一步隔离环境。
CI/CD 自动化测试和部署。
这些原则的核心思想(隔离性、可复现性、安全性)依然重要,只是工具更高效了。
三、Organizing your project
Summary
Using a single module for your application is good for quick projects.
Using a package for your application is good for projects with views, models, forms and other components.
config.py
requirements.txt
run.py
instance/
config.py
yourapp/
__init__.py
views.py
models.py
forms.py
static/
templates/
Blueprints are a great way to organize projects with several distinct components.
✅ 至今仍然适用的原则
1. 单模块适合小型项目
适用场景:
快速原型、微服务或简单 API(如一个
app.py
包含路由和逻辑)。仍常见于教程、实验性代码或小型工具开发。
现代补充:
即使单文件,也应遵循模块化设计(如分离路由、业务逻辑)。
可搭配
Flask 2.0
的async
支持提升简单应用的性能。
2. 包结构适合复杂项目
为什么有效:
分层架构(如
models/
,views/
,services/
)仍是中大型项目的标准实践。支持更好的可测试性和可维护性。
现代改进:
结合
工厂模式
(Factory Pattern)创建应用实例(通过create_app()
函数)。使用
Flask-SQLAlchemy
或Flask-Pydantic
等扩展规范组件交互。
3. 蓝图(Blueprints)组织多组件
核心优势:
模块化路由:将不同功能(如用户认证、API 版本)拆分为独立蓝图。
资源隔离:每个蓝图可拥有自己的模板、静态文件。
现代实践:
在微服务架构中,蓝图仍用于单体应用内的功能分区。
结合
Flask-RESTX
或Flask-Smorest
构建结构化 API。
⚠️ 需要调整或谨慎使用的原则
1. 单模块的局限性
问题场景:
随着项目增长,单文件难以维护(路由、模型、逻辑混杂)。
缺乏明确的依赖管理,易导致代码臃肿。
替代方案:
即使小型项目,也建议拆分为
app.py
+extensions.py
+config.py
等。
2. 纯包结构的冗余性
旧问题:
传统包结构(如myapp/__init__.py
+myapp/views.py
)可能导致过度分层。现代优化:
按功能垂直拆分(如
auth/
,blog/
子包),而非按技术分层(models/
,views/
)。使用
Dependency Injection
替代隐式导入(减少循环依赖)。
3. 蓝图的替代方案
新兴趋势:
FastAPI 的 APIRouter:类似蓝图但更轻量,适合纯 API 项目。
微服务拆分:若组件独立性极强,可直接拆分为独立服务(而非蓝图)。
注意:
蓝图仍适用于 Flask 单体应用,但需避免过度设计(如嵌套蓝图)。
🔧 2023 年推荐的项目组织方式
中型项目(包 + 蓝图)
myapp/
├── __init__.py # create_app() 工厂函数
├── auth/ # 认证蓝图
│ ├── routes.py
│ └── models.py
├── blog/ # 博客蓝图
│ ├── routes.py
│ └── templates/
├── extensions.py # 数据库、缓存等扩展
└── config.py
大型项目(按功能垂直拆分)
myapp/
├── core/ # 核心逻辑
├── api/ # API 蓝图(可进一步拆分为 v1/, v2/)
├── cli/ # 命令行工具
├── tests/ # 按功能匹配的测试结构
└── config/
├── dev.py
└── prod.py
总结:如何应用这些原则 today?
坚持核心思想:
简单项目用单文件,复杂项目用包+蓝图。
模块化设计始终重要。
现代调整:
优先按功能(而非技术)划分代码。
结合工厂模式和依赖注入提升灵活性。
评估替代方案:
纯 API 项目可考虑 FastAPI;
超大型应用直接拆微服务。
Flask 的这些原则仍具指导意义,但需根据项目规模和团队需求灵活调整。
四、configuration
Summary
A simple app may only need one configuration file: config.py.
DEBUG = True # Turns on debugging features in Flask
BCRYPT_LOG_ROUNDS = 12 # Configuration for the Flask-Bcrypt extension
MAIL_FROM_EMAIL = "robert@example.com" # For use in application emails
# app.py or app/__init__.py
from flask import Flask
app = Flask(__name__)
app.config.from_object('config')
# Now we can access the configuration variables via app.config["VAR_NAME"].
Instance folders can help us hide secret configuration values.
config.py
requirements.txt
run.py
instance/
config.py
yourapp/
__init__.py
models.py
views.py
templates/
static/
# app.py or app/__init__.py
app = Flask(__name__, instance_relative_config=True)
app.config.from_object('config')
app.config.from_pyfile('config.py')
Instance folders can be used to alter an application’s configuration for a specific environment.
We should use environment variables and
app.config.from_envvar()
for more complicated environment-based configurations.
1. 简单应用只需一个 config.py
✅ 仍有优势,但需谨慎使用
适用场景:
快速原型开发、小型项目、个人工具。
无需多环境部署或敏感信息的场景。
优势:
简单直接,适合低复杂度项目。
过时点:
硬编码敏感信息:现代开发中,直接写密码/API 密钥到文件是严重的安全反模式。
环境适应性差:无法轻松切换开发/生产配置。
改进建议:
即使小型项目,也应避免硬编码敏感信息,至少用.env
文件 +python-dotenv
。
2. 使用 Instance Folders 隐藏敏感配置
⚠️ 部分过时,仍有特定用途
适用场景:
传统 Flask 项目(非云原生部署)。
需要本地开发与生产配置分离的简单场景。
优势:
避免敏感配置提交到版本控制(如
instance/config.py
在.gitignore
中)。
过时点:
云原生兼容性差:现代部署(Docker/K8s)更依赖环境变量或 Secrets 管理。
不够动态:需手动维护不同环境的实例文件夹,不符合自动化流程。
改进建议:
云原生项目中,优先使用环境变量或 Secrets 管理工具(如 Vault、K8s Secrets)。
3. 使用环境变量 + app.config.from_envvar()
✅ 仍是黄金标准
适用场景:
任何需要多环境(开发/测试/生产)支持的项目。
云原生、容器化(Docker/K8s)或 Serverless 部署。
优势:
安全性:敏感信息通过运行时注入,不暴露在代码中。
灵活性:无需修改代码即可切换环境配置。
符合 12-Factor App:被现代 DevOps 工具链(CI/CD)原生支持。
注意事项:
需配合工具(如
python-dotenv
)简化本地开发。
现代扩展:
使用更高级的配置库(如dynaconf
、pydantic-settings
)支持多格式(YAML/JSON)和类型校验。
4. 混合配置策略的演进
✅ 推荐实践:分层配置
现代项目通常结合以下方式:
默认配置:
config.py
或settings.toml
(非敏感值)。环境覆盖:通过环境变量或
.env
文件(开发环境)。生产机密:通过云平台 Secrets 管理(如 AWS Secrets Manager)。
优势:
兼顾开发便利性与生产安全性。
避免“过时”方案的硬编码问题。
总结:哪些原则已过时?
原则 | 是否过时 | 原因 | 替代方案 |
---|---|---|---|
单一 config.py |
部分过时 | 硬编码不安全 | 默认配置 + 环境变量覆盖 |
Instance Folders | 基本过时 | 云原生兼容性差 | 环境变量/Secrets |
环境变量 | 仍是主流 | 符合现代实践 | 无,可扩展工具链 |
现代最佳实践建议:
简单项目:
.env
+python-dotenv
+config.py
(仅非敏感配置)。复杂项目:环境变量 +
dynaconf
/pydantic-settings
+ 云 Secrets 管理。彻底弃用:硬编码敏感信息、依赖实例文件夹切换环境。
云原生时代,环境变量和集中式 Secrets 管理已成为事实标准,而传统方法仅适用于遗留项目或极简场景。
五、Advanced patterns for views and routing
Summary
The
@login_required
decorator from Flask-Login helps you limit views to authenticated users.
# app.py
from flask import render_template
from flask_login import login_required, current_user
@app.route('/')
def index():
return render_template("index.html")
@app.route('/dashboard')
@login_required
def account():
return render_template("account.html")
The Flask-Cache extension gives you a bunch of decorators to implement various methods of caching.
# app.py
from flask_cache import Cache
from flask import Flask
app = Flask()
# We'd normally include configuration settings in this call
cache = Cache(app)
@app.route('/')
@cache.cached(timeout=60)
def index():
[...] # Make a few database calls to get the information we need
return render_template(
'index.html',
latest_posts=latest_posts,
recent_users=recent_users,
recent_photos=recent_photos
)
We can develop custom view decorators to help us organize our code and stick to DRY (Don’t Repeat Yourself) coding principles.
# myapp/util.py
from functools import wraps
from datetime import datetime
from flask import flash, redirect, url_for
from flask_login import current_user
def check_expired(func):
@wraps(func)
def decorated_function(*args, **kwargs):
if datetime.utcnow() > current_user.account_expires:
flash("Your account has expired. Update your billing info.")
return redirect(url_for('account_billing'))
return func(*args, **kwargs)
return decorated_function
Custom URL converters can be a great way to implement creative features involving URL’s.
1. @login_required
装饰器(Flask-Login)
✅ 仍在广泛使用
现状:
Flask-Login
仍是 Flask 生态中最主流的身份验证库,@login_required
是限制未登录访问的标准方式。配合现代前端(如 JWT、OAuth2)时,可能改用权限校验中间件(如
Flask-JWT-Extended
),但传统 Session 登录场景仍依赖此装饰器。
优势:
简单直观,适合服务端渲染(SSR)或混合应用。
注意点:
若项目完全基于 API(如 React/Vue 前端),可能改用 JWT 的
@jwt_required
(但逻辑类似)。
2. Flask-Cache 的缓存装饰器
⚠️ 已过时,被替代
现状:
Flask-Cache
官方已停止维护,其继任者是Flask-Caching
(支持更现代的缓存后端,如 Redis、Memcached)。装饰器(如
@cache.cached()
)仍在使用,但底层实现更高效。
现代替代方案:
使用
Flask-Caching
+ Redis 实现分布式缓存。对于 API 项目,可能直接用 CDN 缓存 或 HTTP 缓存头(如
Cache-Control
)。
关键变化:
缓存逻辑从“代码装饰器”扩展到“基础设施层”(如 Redis、Nginx)。
3. 自定义视图装饰器(DRY 原则)
✅ 仍是核心实践
现状:
自定义装饰器(如
@check_permissions
、@log_requests
)仍是 Flask 中复用逻辑的推荐方式。在微服务或复杂项目中,可能结合 中间件(Middleware) 或 蓝图(Blueprint)钩子 实现类似功能。
优势:
保持代码简洁,符合 DRY 原则(例如统一处理权限、日志、限流)。
注意点:
过度使用装饰器可能导致代码可读性下降(需权衡)。
4. 自定义 URL 转换器
✅ 仍有用,但需求减少
现状:
Flask 内置的 URL 转换器(如
int
、string
)已覆盖大部分场景,自定义转换器(如@app.url_value_preprocessor
)需求减少。现代 RESTful API 更依赖 标准化的 URL 设计(如
/users/<uuid:user_id>
),而非复杂自定义规则。
适用场景:
需要特殊路由逻辑(如动态子域名、Slug 校验)时仍有用武之地。
替代趋势:
复杂路由逻辑可能移交到 API Gateway(如 Kong、Traefik)或前端路由(React/Vue Router)。
总结:哪些原则已过时?
原则 | 是否过时 | 原因 | 现代替代方案 |
---|---|---|---|
@login_required |
仍主流 | 无更优替代 | 兼容 JWT 时需调整 |
Flask-Cache 装饰器 | 已过时 | 项目废弃 | Flask-Caching + Redis |
自定义装饰器 | 仍推荐 | DRY 原则核心 | 可结合中间件 |
自定义 URL 转换器 | 边缘化 | 需求减少 | 标准化路由或 API Gateway |
现代 Flask 开发建议
身份验证:
传统 SSR:
Flask-Login
+@login_required
。纯 API:
Flask-JWT-Extended
+@jwt_required
。
缓存:
使用
Flask-Caching
+ Redis,而非旧版Flask-Cache
。
代码复用:
优先用装饰器,复杂场景可搭配 蓝图的
before_request
或 中间件。
路由设计:
遵循 RESTful 规范,非必要不自定义 URL 转换器。
Flask 的轻量级哲学使其核心原则(如装饰器)依然有效,但工具链(如缓存、身份验证)需跟随生态演进。
六、Blueprints
Summary
A blueprint is a collection of views, templates, static files and other extensions that can be applied to an application.
Blueprints are a great way to organize your application.
In a divisional structure, each blueprint is a collection of views, templates and static files which constitute a particular section of your application.
facebook/
__init__.py
templates/
layout.html
home/
layout.html
index.html
about.html
signup.html
login.html
dashboard/
layout.html
news_feed.html
welcome.html
find_friends.html
profile/
layout.html
timeline.html
about.html
photos.html
friends.html
edit.html
settings/
layout.html
privacy.html
security.html
general.html
views/
__init__.py
home.py
dashboard.py
profile.py
settings.py
static/
style.css
logo.png
models.py
In a functional structure, each blueprint is just a collection of views. The templates are all kept together, as are the static files.
To use a blueprint, you define it then register it on the application by calling
Flask.register_blueprint().
.
# facebook/__init__.py
from flask import Flask
from .views.profile import profile
app = Flask(__name__)
app.register_blueprint(profile)
You can define a dynamic URL prefix that will be applied to all routes in a blueprint.
# facebook/views/profile.py
from flask import Blueprint, render_template
profile = Blueprint('profile', __name__, url_prefix='/<user_url_slug>')
# [...]
# facebook/__init__.py
from flask import Flask
from .views.profile import profile
app = Flask(__name__)
app.register_blueprint(profile, url_prefix='/<user_url_slug>')
You can also define a dynamic subdomain for all routes in a blueprint.
Refactoring a growing application to use blueprints can be done in five relatively small steps.
1. 蓝图(Blueprint)作为应用组件集合
✅ 仍是核心模式
现状:
蓝图仍然是 Flask 中模块化组织代码的标准方式,包含视图、模板、静态文件等。
在微服务架构中,大型应用可能拆分为多个独立服务,但单体应用内仍依赖蓝图分模块。
优势:
解耦功能模块(如
auth_bp
、admin_bp
),支持多人协作开发。
现代调整:
结合 工厂模式(Application Factory) 动态注册蓝图,更适合测试和扩展。
2. 组织方式:功能型(Functional) vs 分区型(Divisional)
⚠️ 分区型(Divisional)更主流,功能型(Functional)边缘化
分区型(Divisional):
仍广泛使用:每个蓝图是一个完整功能模块(如
users_bp
包含视图、模板、静态文件)。适合大多数项目,符合“高内聚低耦合”原则。
功能型(Functional):
基本过时:将模板/静态文件全局集中管理,视图按逻辑拆分(如
auth_views_bp
、profile_views_bp
)。缺点:模板和静态文件难以维护,不符合组件化趋势。
现代实践:
优先用分区型,甚至进一步拆分为 独立 Python 包(如flask-admin
的插件化设计)。
3. 动态 URL 前缀/子域名
✅ 仍在使用,但需求减少
动态 URL 前缀(
url_prefix
):常用:如 API 版本控制(
/api/v1/
)或多租户路径隔离(/tenant/<id>/
)。
动态子域名(
subdomain
):边缘化:现代架构中,子域名路由通常由 反向代理(Nginx) 或 API Gateway(如 Kong)处理,而非应用层。
适用场景:
简单项目可直接用 Flask 子域名功能,复杂场景移交基础设施层。
4. 重构为蓝图的步骤
✅ 仍是标准流程
现状:
将大型应用拆分为蓝图的步骤(如逐步迁移视图、模板)未过时,但现代项目更倾向于 从一开始设计蓝图。
现代改进:
结合 模块化工厂模式(
create_app()
)动态注册蓝图,支持环境差异化配置。
5. 蓝图的注册方式(register_blueprint
)
✅ 语法未变,但注册时机演进
传统方式:
直接在模块层调用
app.register_blueprint()
。
现代方式:
在 应用工厂 中注册,或通过 插件系统(如
flask-sqlalchemy
的init_app()
模式)延迟绑定。
总结:哪些原则已过时?
原则 | 是否过时 | 原因 | 现代替代方案 |
---|---|---|---|
蓝图作为模块化单元 | 仍主流 | 无可替代 | 无 |
功能型(Functional)结构 | 已过时 | 维护性差 | 分区型(Divisional) |
动态子域名 | 边缘化 | 由基础设施处理 | Nginx/API Gateway |
直接全局注册蓝图 | 不推荐 | 缺乏灵活性 | 应用工厂模式 |
现代 Flask 蓝图最佳实践
设计阶段:
按 业务边界 划分蓝图(如
auth_bp
、order_bp
),每个蓝图包含自己的视图、模板、静态文件。
代码结构:
/app ├── /auth # 蓝图模块 │ ├── __init__.py # 创建蓝图对象 │ ├── routes.py # 视图 │ ├── templates/ # 蓝图专属模板 │ └── static/ # 蓝图专属静态文件 ├── /orders │ └── ... # 同上 └── create_app.py # 应用工厂
注册时机:
在
create_app()
中动态注册蓝图,支持测试和配置隔离:
def create_app(config):
app = Flask(__name__)
app.register_blueprint(auth_bp)
app.register_blueprint(orders_bp, url_prefix='/orders')
return app
进阶场景:
将蓝图发布为 独立 PyPI 包(如
flask-admin
),通过pip
安装复用。
何时不需要蓝图?
极简应用:单文件 Flask 应用(如原型开发)。
微服务架构:功能已拆分为独立服务,单体内部无需再分蓝图。
Flask 蓝图的核心思想(模块化)并未过时,但实现细节需结合现代架构(如工厂模式、基础设施分层)优化。
七、Templates
Summary
Use Jinja for templating.
Jinja has two kinds of delimeters:
{% ... %}
and{{ ... }}
. The first one is used to execute statements such as for-loops or assign values, the latter prints the result of the contained expression to the template.
{# _myapp/templates/layout.html_ #}
<!DOCTYPE html>
<html lang="en">
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
{% block body %}
<h1>This heading is defined in the parent.</h1>
{% endblock %}
</body>
</html>
Templates should go in myapp/templates/ — i.e. a directory inside of the application package.
I recommend that the structure of the templates/ directory mirror the URL structure of the app.
You should have a top-level layout.html in myapp/templates as well as one for each section of the site. The latter extend the former.
templates/
layout.html
index.html
about.html
profile/
layout.html
index.html
photos.html
admin/
layout.html
index.html
analytics.html
Macros are like functions made-up of template code.
{# myapp/templates/layout.html #}
{% from "macros.html" import nav_link with context %}
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<title>My application</title>
{% endblock %}
</head>
<body>
<ul class="nav-list">
{{ nav_link('home', 'Home') }}
{{ nav_link('about', 'About') }}
{{ nav_link('contact', 'Get in touch') }}
</ul>
{% block body %}
{% endblock %}
</body>
</html>
Filters are functions made-up of Python code and used in templates.
1. 使用 Jinja2 作为模板引擎
✅ 仍是 Flask 的黄金标准
现状:
Jinja2 仍是 Flask 默认且官方推荐的模板引擎,与 Flask 深度集成。
现代前端框架(如 React/Vue)的兴起减少了服务端渲染(SSR)的使用,但 Jinja2 在 SSR 场景中不可替代。
优势:
语法简洁,支持模板继承、宏等高级功能。
适合内容型网站(如博客、CMS)或需要 SEO 的页面。
注意点:
纯 API 项目可能完全不需要 Jinja2。
2. Jinja2 分隔符 {% ... %}
和 {{ ... }}
✅ 语法未变,仍是核心
现代调整:
新增
{% ... %}
的扩展用法(如{% set x = namespace() %}
),但基础逻辑不变。前端开发者可能更熟悉 JSX/Vue 的
{{ }}
,但 Jinja2 的语义保持一致。
3. 模板目录应位于 myapp/templates/
⚠️ 仍适用,但灵活性增加
传统实践:
Flask 默认从
templates/
加载模板,符合“约定优于配置”原则。
现代演进:
可通过
template_folder
参数自定义路径(如Flask(__name__, template_folder="../frontend/templates")
)。大型项目可能将模板拆分为 独立包(如共享模板库)。
建议:除非有特殊需求,否则仍推荐默认目录结构。
4. 模板目录结构应镜像 URL 结构
❌ 已过时(仅适用于简单项目)
过时原因:
现代项目更倾向于 按功能模块组织模板(如
templates/auth/login.html
),而非严格匹配 URL(如/auth/login
)。RESTful API 和前端框架的普及使得 URL 结构与模板解耦。
例外:
内容型网站(如新闻分类)可能仍保留部分镜像结构。
5. 顶层 layout.html
+ 区块模板继承
✅ 仍是最佳实践
现状:
基模板(
layout.html
)定义通用结构(如导航栏、页脚),子模板通过{% extends %}
和{% block %}
覆盖特定区块。与前端框架的“组件化”思想一致(如 Vue 的
<slot>
)。
优势:
避免重复代码,符合 DRY 原则。
6. 宏(Macros)作为模板函数
✅ 仍有用,但使用减少
现状:
宏适合复用模板片段(如渲染表单字段),但在以下场景中被替代:
前端框架(如 React/Vue)通过组件化实现更强大的复用。
复杂逻辑更适合移回后端(通过 API 返回结构化数据)。
适用场景:
服务端渲染中需要重复使用的 UI 元素(如分页控件)。
7. 过滤器(Filters)
✅ 仍有用,但需谨慎使用
现状:
内置过滤器(如
|safe
、|capitalize
)仍常用。自定义过滤器适合简单文本处理(如日期格式化),但复杂逻辑应优先在 后端处理 或通过 前端工具库(如
day.js
)。
风险:
过度使用过滤器会导致模板臃肿,违背“逻辑与表现分离”原则。
总结:哪些原则已过时?
原则 | 是否过时 | 原因 | 现代替代方案 |
---|---|---|---|
使用 Jinja2 | 仍主流 | 无更优替代 | 纯 API 项目可省略 |
分隔符语法 | 未变 | 语法稳定 | 无 |
默认 templates/ 目录 |
仍适用 | 约定优于配置 | 支持自定义路径 |
模板镜像 URL 结构 | 已过时 | 灵活性差 | 按功能模块组织 |
模板继承(layout.html ) |
仍最佳实践 | DRY 原则 | 类似前端组件化 |
宏(Macros) | 边缘化 | 前端框架替代 | Vue/React 组件 |
过滤器(Filters) | 有限使用 | 逻辑应后移 | 后端处理或前端工具库 |
现代 Jinja2 模板开发建议
组织方式:
按功能模块划分模板目录(如
templates/auth/
、templates/blog/
),而非机械匹配 URL。
逻辑分离:
复杂计算通过 视图函数 预处理,模板仅负责渲染。
组件化:
使用
{% include %}
或宏封装可复用 UI 片段(如按钮、卡片)。
安全性:
始终用
|safe
标记可信的 HTML 内容,避免 XSS。
结合现代工具:
开发阶段启用
TEMPLATES_AUTO_RELOAD=True
自动刷新模板。
何时不用 Jinja2?
纯 API 项目(无服务端渲染)。
前后端完全分离(前端使用 React/Vue)。
Jinja2 的核心功能(模板继承、变量渲染)依然不可替代,但需根据项目架构调整使用方式。
八、Static files
Summary
Static files go in the static/ directory.
myapp/
__init__.py
static/
templates/
views/
models.py
run.py
Separate third-party libraries from your own static files.
static/
css/
lib/
bootstrap.css
style.css
home.css
admin.css
js/
lib/
jquery.js
home.js
admin.js
img/
logo.svg
favicon.ico
Specify the location of your favicon in your templates.
Use Flask-Assets to insert static files in your templates.
# myapp/util/assets.py
from flask_assets import Bundle, Environment
from .. import app
bundles = {
'home_js': Bundle(
'js/lib/jquery-1.10.2.js',
'js/home.js',
output='gen/home.js'),
'home_css': Bundle(
'css/lib/reset.css',
'css/common.css',
'css/home.css',
output='gen/home.css'),
'admin_js': Bundle(
'js/lib/jquery-1.10.2.js',
'js/lib/Chart.js',
'js/admin.js',
output='gen/admin.js'),
'admin_css': Bundle(
'css/lib/reset.css',
'css/common.css',
'css/admin.css',
output='gen/admin.css')
}
assets = Environment(app)
assets.register(bundles)
Flask-Assets can compile, combine and compress your static files.
仍然适用的原则 ✅
Static files go in the static/ directory
Flask 仍然默认使用
static/
目录存放静态文件,这是官方推荐的做法
Separate third-party libraries from your own static files
仍然建议将第三方库(如 jQuery、Bootstrap)和自己的静态文件分开管理
Specify the location of your favicon in your templates
仍然需要在模板中正确指定 favicon 路径
部分过时/有更好替代方案的原则 ⚠️
Use Flask-Assets to insert static files in your templates
仍然可用但不是主流选择,现代前端工作流更倾向于使用:
Webpack/Vite 等现代打包工具
Flask 原生
url_for('static', ...)
方式CDN 直接引入前端库
Flask-Assets can compile, combine and compress your static files
功能仍然存在但不再是首选方案,现代替代方案包括:
使用 npm/yarn/pnpm + Webpack/Vite/Rollup
使用 Flask-Minify 等专门压缩扩展
云服务(如 AWS CloudFront)自动压缩
现代补充建议 ✨
考虑使用
flask-static-compress
或flask-minify
进行压缩对于复杂项目,建议采用前后端分离架构
静态文件建议通过 CDN 分发
现代前端框架(React/Vue)通常有自己的静态文件处理方式
Flask-Assets 虽然仍能工作,但在 2023 年后的新项目中已经很少被用作主要解决方案,除非是在维护传统项目。
九、Storing data
Summary
Use SQLAlchemy to work with relational databases.
# ourapp/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__, instance_relative_config=True)
app.config.from_object('config')
app.config.from_pyfile('config.py')
db = SQLAlchemy(app)
# ourapp/models.py
from . import db
class Engine(db.Model):
# Columns
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(128))
thrust = db.Column(db.Integer, default=0)
Use Flask-SQLAlchemy to work with SQLAlchemy.
Alembic helps you migrate your data between schema changes.
ourapp/
alembic.ini
alembic/
env.py
README
script.py.mako
versions/
3512b954651e_add_account.py
2b1ae634e5cd_add_order_id.py
3adcc9a56557_rename_username_field.py
myapp/
__init__.py
views.py
models.py
templates/
run.py
config.py
requirements.txt
You can use NoSQL databases with Flask, but the methods and tools vary between engines.
Back up your data!
仍然适用的原则 ✅
Use SQLAlchemy to work with relational databases
SQLAlchemy 仍然是 Python 生态中最主流、最强大的 ORM,Flask 项目中的首选方案
核心功能(ORM/SQL Expression)设计经受了时间考验
Use Flask-SQLAlchemy to work with SQLAlchemy
Flask-SQLAlchemy 仍然是官方推荐的集成方案(最新版本 3.1.x)
提供了
db.session
管理和 Flask 集成等便利功能
Alembic helps you migrate your data between schema changes
Alembic 仍然是 SQLAlchemy 生态的标准迁移工具
现代改进:支持异步(Alembic 1.11+)、更好的 DDL 事务控制
Back up your data!
永恒真理,现在更多通过云数据库的自动备份功能实现(如 AWS RDS 快照)
需要更新的原则 ⚠️
You can use NoSQL databases with Flask, but the methods and tools vary between engines
仍然正确,但现代变化:
MongoDB:推荐官方驱动
PyMongo
(Flask-PyMongo 已不维护)Redis:直接使用
redis-py
,部分场景可用Flask-Caching
新兴数据库:如 ClickHouse 有专用 Python 驱动
趋势:NoSQL 不再强调 "Flask 扩展",而是直接使用各数据库的官方 SDK
现代补充建议 ✨
异步支持:
SQLAlchemy 2.0+ 全面支持 async(需配合
asyncpg
/aiomysql
)Flask 本身不支持异步,但可通过 Quart(Flask 异步克隆)实现
替代方案:
简单项目可考虑
PeeWee
或SQLModel
(SQLAlchemy + Pydantic)Django 开发者可能更喜欢
Flask-Marshmallow
序列化
云原生趋势:
无服务器场景考虑 Serverless 数据库(如 PlanetScale、Neon)
分布式 SQL(如 CockroachDB)有完善 SQLAlchemy 支持
开发体验:
使用
Flask-Migrate
简化 Alembic 操作(flask db migrate
)推荐
SQLAlchemy 2.0
声明式表配置(更简洁的语法)
安全建议:
一定要启用 SQLAlchemy 的
echo=False
生产环境使用
scoped_session
避免多线程问题
过时/不推荐的做法 🚫
避免旧版 SQLAlchemy 1.3 的写法(如
query.get()
已弃用)不再推荐使用
Flask-MongoEngine
等封装过度的 NoSQL 扩展避免手动拼接 SQL(SQLAlchemy Core 已能处理绝大多数场景)
现代 Flask 项目数据库选择建议:PostgreSQL + SQLAlchemy 2.0 + Alembic 仍是黄金组合。
十、Handling forms
Summary
Forms can be scary from a security perspective.
# ourapp/forms.py
from flask_wtf import Form
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired, Email
class EmailPasswordForm(Form):
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
WTForms (and Flask-WTF) make it easy to define, secure and render your forms.
Use the CSRF protection provided by Flask-WTF to secure your forms.
{# ourapp/templates/login.html #}
{% extends "layout.html" %}
<html>
<head>
<title>Login Page</title>
</head>
<body>
<form action="{{ url_for('login') }}" method="post">
<input type="text" name="email" />
<input type="password" name="password" />
{{ form.csrf_token }}
</form>
</body>
</html>
You can use Flask-WTF to protect AJAX calls against CSRF attacks too.
Define custom form validators to keep validation logic out of your views.
Use the WTForms field rendering to render your form’s HTML so you don’t have to update it every time you make some changes to the form definition.
仍然适用的原则 ✅
Forms can be scary from a security perspective
表单安全仍是重中之重(CSRF/XSS/SQL 注入等风险依然存在)
Use the CSRF protection provided by Flask-WTF to secure your forms
Flask-WTF 的 CSRF 保护仍是标准方案(最新版本 1.2.x)
现代补充:REST API 推荐改用 JWT/OAuth2 + SameSite cookies
Define custom form validators to keep validation logic out of your views
验证逻辑与视图分离仍是最佳实践
现代扩展:可结合 Pydantic 进行更复杂的验证
需要更新的原则 ⚠️
WTForms (and Flask-WTF) make it easy to define, secure and render your forms
仍然可用但不再是唯一选择,现代替代方案:
前端框架(React/Vue)直接处理表单 + Flask 仅作 API 验证
使用 Pydantic 进行数据验证(尤其适合 API 项目)
简单场景可用
flask.request.get_json()
直接处理
You can use Flask-WTF to protect AJAX calls against CSRF attacks too
仍然有效但现代方案更倾向于:
对 API 使用 JWT + CORS 限制
启用
SameSite=Strict
cookie 策略框架内置 CSRF(如 Next.js/Angular 自带防护)
Use the WTForms field rendering to render your form’s HTML
服务器端渲染表单逐渐减少,现代趋势:
前端动态渲染(React/Vue/Svelte 表单组件库)
仅用 WTForms 做验证,前端单独实现 UI
HTMX 等新技术实现渐进式增强
现代补充建议 ✨
混合验证架构:
# 结合 Pydantic 和 WTForms 的优点 from pydantic import BaseModel from flask_wtf import FlaskForm class APIModel(BaseModel): # API 请求验证 name: str age: int class WebForm(FlaskForm): # 传统网页表单 name = StringField(validators=[DataRequired()])
安全增强:
始终启用
WTF_CSRF_ENABLED = True
(默认已开启)对敏感操作添加二次验证(如 CAPTCHA)
现代替代工具:
前端表单:Formik(React)、VeeValidate(Vue)
验证库:Pydantic、marshmallow
安全:Flask-Talisman(强制 HTTPS/HSTS)
性能优化:
对静态表单考虑缓存渲染结果
使用
flask-caching
缓存验证规则
过时/不推荐的做法 🚫
避免完全依赖 WTForms 渲染前端(难以适应现代 UI 需求)
不要手动拼接 HTML 表单(易导致 XSS 漏洞)
避免在 AJAX 中直接提交
application/x-www-form-urlencoded
(推荐 JSON)
当前推荐方案
场景 | 推荐工具 |
---|---|
传统多页应用 | Flask-WTF + WTForms |
SPA 前后端分离 | Pydantic + 前端表单库 |
混合应用(HTMX) | WTForms 验证 + 部分渲染 |
关键结论:WTForms 仍然安全可靠,但现代项目更倾向于将表单逻辑交给前端,后端专注数据验证。对于新项目,建议优先考虑 Pydantic + 前端框架的组合。
十一、Patterns for handling users
Summary
Use the itsdangerous package to create and validate tokens sent to an email address.
# ourapp/util/security.py
from itsdangerous import URLSafeTimedSerializer
from .. import app
ts = URLSafeTimedSerializer(app.config["SECRET_KEY"])
# ourapp/views.py
from flask import redirect, render_template, url_for
from . import app, db
from .forms import EmailPasswordForm
from .util import ts, send_email
@app.route('/accounts/create', methods=["GET", "POST"])
def create_account():
form = EmailPasswordForm()
if form.validate_on_submit():
user = User(
email = form.email.data,
password = form.password.data
)
db.session.add(user)
db.session.commit()
# Now we'll send the email confirmation link
subject = "Confirm your email"
token = ts.dumps(self.email, salt='email-confirm-key')
confirm_url = url_for(
'confirm_email',
token=token,
_external=True)
html = render_template(
'email/activate.html',
confirm_url=confirm_url)
# We'll assume that send_email has been defined in myapp/util.py
send_email(user.email, subject, html)
return redirect(url_for("index"))
return render_template("accounts/create.html", form=form)
{# ourapp/templates/email/activate.html #}
Your account was successfully created. Please click the link below<br>
to confirm your email address and activate your account:
<p>
<a href="{{ confirm_url }}">{{ confirm_url }}</a>
</p>
<p>
--<br>
Questions? Comments? Email hello@myapp.com.
</p>
You can use these tokens to validate emails when a user creates an account, changes their email or forgets their password.
# ourapp/views.py
@app.route('/confirm/<token>')
def confirm_email(token):
try:
email = ts.loads(token, salt="email-confirm-key", max_age=86400)
except:
abort(404)
user = User.query.filter_by(email=email).first_or_404()
user.email_confirmed = True
db.session.add(user)
db.session.commit()
return redirect(url_for('signin'))
Authenticate users using the Flask-Login extension to avoid dealing with a bunch of session management stuff yourself.
Always think about how a malicious user could abuse your app to do things that you didn’t intend.
仍然适用的原则 ✅
Authenticate users using Flask-Login
Flask-Login(最新版本 0.6.x)仍是管理用户会话的事实标准
核心功能稳定:
@login_required
、current_user
、remember_me
等现代补充:支持自定义用户加载器(包括异步场景)
Always think about security
"考虑恶意用户行为"是永恒原则,现代威胁模型新增:
API 滥用(暴力破解/爬虫)→ 需增加速率限制(Flask-Limiter)
密码 spraying 攻击→ 强制 MFA(如 Flask-Dance 支持 OAuth)
供应链攻击→ 依赖项扫描(pip-audit)
需要更新的原则 ⚠️
Use itsdangerous for email tokens
仍然可用但不再是首选方案,现代替代方案:
专用令牌库:PyJWT(RFC 7519 标准 JWT)
安全增强:使用
cryptography
直接生成加密令牌云服务:AWS Cognito/Auth0 等托管服务
仅推荐 itsdangerous 用于简单场景(如开发环境)
现代补充建议 ✨
认证架构升级
# 现代认证组合示例(2024)
from flask_jwt_extended import JWTManager # REST API
from flask_principal import Principal # 权限管理
from authlib.integrations.flask_client import OAuth # 第三方登录
jwt = JWTManager(app) # 替代部分 itsdangerous 场景
oauth = OAuth(app)
关键实践
密码重置/邮件验证
改用 时间受限的 JWT(
flask-jwt-extended
)必须使用 一次性令牌(即使 itsdangerous 也需自行实现)
会话管理
启用
SESSION_PROTECTION = "strong"
(Flask-Login)生产环境必须使用
Secure
/HttpOnly
cookies
现代威胁防护
# 安全头设置(Flask-Talisman) talisman = Talisman( app, content_security_policy=..., force_https=True )
审计工具
使用
bandit
扫描安全漏洞依赖项检查:
safety check
或 GitHub Dependabot
过时/不推荐的做法 🚫
避免直接使用 itsdangerous 处理敏感操作(如密码重置)
不要自行实现加密算法(应使用
passlib
或bcrypt
)避免将 Flask-Login 用于纯 API 项目(应改用 JWT)
2024 年推荐方案
场景 | 工具选择 |
---|---|
传统网页应用 | Flask-Login + Flask-WTF CSRF |
REST API | flask-jwt-extended + OAuth2 |
邮件验证/密码重置 | PyJWT(而非 itsdangerous) |
第三方登录 | Authlib/OAuthLib |
核心建议:
新项目优先考虑 JWT + OAuth 2.0 生态
维护旧项目时可继续使用 itsdangerous,但需增加速率限制
必须实施 密码哈希(argon2id/bcrypt) + HTTPS 强制跳转
十二、Deployment
Summary
Three good choices for hosting Flask apps are AWS EC2, Heroku and Digital Ocean.
The basic deployment stack for a Flask application consists of the app, an application runner like Gunicorn and a reverse proxy like Nginx.
(ourapp)$ gunicorn rocket:app
2014-03-19 16:28:54 [62924] [INFO] Starting gunicorn 18.0
2014-03-19 16:28:54 [62924] [INFO] Listening at: http://127.0.0.1:8000 (62924)
2014-03-19 16:28:54 [62924] [INFO] Using worker: sync
2014-03-19 16:28:54 [62927] [INFO] Booting worker with pid: 62927
(ourapp)$ gunicorn rocket:app -p rocket.pid -D
(ourapp)$ cat rocket.pid
63101
(ourapp)$ kill -HUP `cat rocket.pid`
(ourapp)$ kill `cat rocket.pid`
Gunicorn should sit behind Nginx and listen on 127.0.0.1 (internal requests) not 0.0.0.0 (external requests).
Use Werkzeug’s ProxyFix to handle the appropriate proxy headers in your Flask application.
仍然适用的原则 ✅
基础部署架构(App → Gunicorn → Nginx)
传统虚拟机部署仍保持此结构,但现代方案更倾向于容器化
新增要求:Nginx 现在需强制配置
HTTP/2
和TLS 1.3
Gunicorn 监听本地端口
安全规范仍然要求:生产环境必须绑定
127.0.0.1
而非0.0.0.0
现代补充:Kubernetes 环境下改为通过 Service 暴露
Werkzeug 的 ProxyFix
仍然需要处理反向代理头,但现代方案:
from werkzeug.middleware.proxy_fix import ProxyFix app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1)
新增要求:必须显式配置
x_for/x_proto
数量(防伪造)
需要更新的原则 ⚠️
三大托管服务推荐(EC2/Heroku/Digital Ocean)
Heroku:已不再提供免费层(2022年11月取消),性价比下降
Digital Ocean:现更推荐其 App Platform(托管容器服务)
AWS EC2:仍是主流但非最优选择,现代替代方案:
容器服务:AWS ECS/EKS、Google Cloud Run
Serverless:AWS Lambda(Zappa框架)、Vercel
边缘部署:Fly.io、Cloudflare Workers
现代补充建议 ✨
部署架构演进
关键实践升级
运行时选择
同步应用:仍可用 Gunicorn + gevent
异步应用:改用 Uvicorn(ASGI 服务器,支持 HTTP/2)
安全强化
# Nginx 现代配置片段 location / { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header X-Forwarded-Proto $scheme; # 必须设置严格 CSP add_header Content-Security-Policy "default-src 'self'"; }
基础设施即代码
使用 Terraform/Pulumi 管理云资源
部署工具:GitHub Actions > Fabric/Capistrano
性能优化
静态文件移交 CDN(Cloudflare/R2)
启用 Brotli 压缩替代 gzip
过时/不推荐的做法 🚫
避免直接部署裸机/虚拟机(除非有特殊需求)
不要使用
flask run
生产环境(仍常见于教程但极不安全)避免手动配置服务器(应使用 Docker + CI/CD 自动化)
2024 年推荐方案
场景 | 推荐方案 | 优势 |
---|---|---|
快速原型 | Fly.io/Render | 5分钟部署,免费额度 |
传统应用 | Docker + EC2 | 平衡控制力与成本 |
高流量服务 | Kubernetes + GCP | 自动伸缩 |
Serverless | AWS Lambda + API Gateway | 按需计费 |
重要趋势:
容器化已成默认选择(即使小型项目)
Serverless 方案成熟(Cold Start 问题显著改善)
边缘计算兴起(如 Cloudflare Workers 支持 Python)
建议新项目优先考虑:Docker → 托管容器服务(如 Fly.io) 的路径,既保持开发一致性,又降低运维复杂度。