【Flask】Explore-Flask:早期 Flask 生态的实用指南
开源项目: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__.pyviews.pymodels.pyforms.pystatic/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 Flaskapp = 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__.pymodels.pyviews.pytemplates/static/
# app.py or app/__init__.pyapp = 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.pyfrom 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.pyfrom flask_cache import Cache
from flask import Flaskapp = 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 needreturn 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.pyfrom functools import wraps
from datetime import datetimefrom flask import flash, redirect, url_forfrom flask_login import current_userdef 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__.pytemplates/layout.htmlhome/layout.htmlindex.htmlabout.htmlsignup.htmllogin.htmldashboard/layout.htmlnews_feed.htmlwelcome.htmlfind_friends.htmlprofile/layout.htmltimeline.htmlabout.htmlphotos.htmlfriends.htmledit.htmlsettings/layout.htmlprivacy.htmlsecurity.htmlgeneral.htmlviews/__init__.pyhome.pydashboard.pyprofile.pysettings.pystatic/style.csslogo.pngmodels.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__.pyfrom flask import Flask
from .views.profile import profileapp = 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.pyfrom flask import Blueprint, render_templateprofile = Blueprint('profile', __name__, url_prefix='/<user_url_slug>')# [...]
# facebook/__init__.pyfrom flask import Flask
from .views.profile import profileapp = 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.htmlindex.htmlabout.htmlprofile/layout.htmlindex.htmlphotos.htmladmin/layout.htmlindex.htmlanalytics.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__.pystatic/templates/views/models.py
run.py
-
Separate third-party libraries from your own static files.
static/css/lib/bootstrap.cssstyle.csshome.cssadmin.cssjs/lib/jquery.jshome.jsadmin.jsimg/logo.svgfavicon.ico
-
Specify the location of your favicon in your templates.
-
Use Flask-Assets to insert static files in your templates.
# myapp/util/assets.pyfrom flask_assets import Bundle, Environment
from .. import appbundles = {'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__.pyfrom flask import Flask
from flask_sqlalchemy import SQLAlchemyapp = Flask(__name__, instance_relative_config=True)app.config.from_object('config')
app.config.from_pyfile('config.py')db = SQLAlchemy(app)
# ourapp/models.pyfrom . import dbclass Engine(db.Model):# Columnsid = 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.inialembic/env.pyREADMEscript.py.makoversions/3512b954651e_add_account.py2b1ae634e5cd_add_order_id.py3adcc9a56557_rename_username_field.pymyapp/__init__.pyviews.pymodels.pytemplates/run.pyconfig.pyrequirements.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.pyfrom flask_wtf import Form
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired, Emailclass 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 FlaskFormclass APIModel(BaseModel): # API 请求验证name: strage: intclass 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.pyfrom itsdangerous import URLSafeTimedSerializerfrom .. import appts = URLSafeTimedSerializer(app.config["SECRET_KEY"])
# ourapp/views.pyfrom flask import redirect, render_template, url_forfrom . 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 linksubject = "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.pysend_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 = Truedb.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;# 必须设置严格 CSPadd_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) 的路径,既保持开发一致性,又降低运维复杂度。