Flask 的核心只有四件事:路由怎么配、模板去哪找、请求怎么读、响应怎么写。 数据库、表单、登录、迁移,一个都不在里面——全是装上去的插件。 「微」不是说功能少,是说它不替你决定;默认值本身就是一种决定。
这是全书的第 0 章,教材把它放在「第0篇 绪论」里,编号从 0 开始,本课不改。 后面 17 章全部围绕同一个物件侦测应用展开,这一篇先把「Flask 是什么」说清。
一、先从一次请求说起
浏览器敲一个网址,请求发到服务器,服务器把网址交给一个 Python 程序,程序回一段 HTML。 Flask 就站在这条链路中间。 它和服务器之间的约定叫 WSGI:服务器把「请求」打包成一个字典交给应用, 应用回一个「状态码加若干响应头加正文」。 Flask 应用的本质,就是一个符合这条约定的可调用对象。所以它自己能跑,但真要对外服务, 还得有个服务器替它接连接。
教材的 0.1 节强调的就是这一点:Flask 不做服务器该做的事,它只负责「拿到请求之后怎么办」。
二、核心只有四件事
| 管什么 | 谁在管 | 你能改吗 |
|---|---|---|
| 路由怎么配 | Werkzeug | 能,装饰器就是入口 |
| 模板去哪找 | Jinja2 | 能,template_folder 参数 |
| 请求怎么读 | Werkzeug 加 Flask 包装 | 能,request 对象 |
| 响应怎么写 | Flask | 能,返回什么就是什么 |
四件之外的一切——连数据库、做表单校验、管登录状态——都不在核心里,得自己挑组件装上去。 这就是「微框架」的字面意思:核心小,功能靠装。 小有小的代价:装什么、怎么装、装错了怎么办,全是你的事。
三、默认值也是决定
教材有一句话容易被跳过:Flask 会「擅自」替你准备一个静态文件目录。
你真建一个只写了六行的应用,然后打印路由表,会发现里面有两条:你写的那条,
和一条 /static/<path:filename>。
这条不是你写的,是构造 Flask(__name__) 时它照着默认参数加的。
所以「不替你做决定」是有保留的:它只决定四件事,但在这四件事里,它照样会给你默认值。
想收回也简单:Flask(__name__, static_folder=None),那条路由就没了。
四、不替你决定,换来的是什么
| 换来 | 代价 |
|---|---|
| 体积小,导入快 | 每个功能都要自己选组件 |
| 能换掉任何一层 | 换的时候要改的地方不少 |
| 报错信息短,指向清楚 | 出问题往往在插件之间,不在 Flask 里 |
| 适合做 API 与服务 | 做后台管理系统要多写很多 |
反过来说,Django 那种「全都替你决定」的框架,好处是上手就有后台、有 ORM、有目录结构; 代价是改它的决定很费劲。 选框架不是选功能多少,是选「你愿意让出多少决定权」。这句话在下一节会被四个框架各自印证一次。
五、这本书拿 Flask 做什么
路径很清楚:第 1 到 3 章从单文件应用做到带登录的数据库应用, 第 4 到 11 章做一个完整的物件侦测网站,第 12 到 15 章把它改成 API 并部署, 第 16 到 17 章做一个机器学习 API。 所以这本书不是 Flask 手册,是一次完整的交付演练。语法点够用就行, 重点是「怎么把东西做成能交的」。 读的时候有个心理准备:第 2 章、第 8 章、第 17 章的代码会一下长很多,那是正常现象,不是你没跟上。
六、动手 Demo:Flask 替你决定了什么
这一节的 Demo 不写分析,只做一件事:把「默认值」摊开给你看。 先建最小的应用,再打印路由表,然后把默认关掉再打一次。
from flask import Flask, render_template_string
app = Flask(__name__)
@app.route("/")
def index():
return "hello, flask"
for r in sorted(app.url_map.iter_rules(), key=str):
print(r, sorted(r.methods - {"HEAD", "OPTIONS"}))
print(app.test_client().get("/").status_code)
print(app.test_client().get("/nope").status_code)
实跑输出:
--------------------------------------------------------------------
Flask 替你决定了什么
--------------------------------------------------------------------
先看一个能跑的最小应用,六行:
[代码原样]
| from flask import Flask
| app = Flask(__name__)
|
| @app.route("/")
| def index():
| return "hello, flask"
这六行就是全部。没有配置、没有目录约定、没有 settings 文件。
① 路由表:它悄悄给你加了一条
--------------------------------------------------------------------
| 规则 | 方法 | 端点 |
|---|---|---|
| / | GET | index |
| /static/<path:filename> | GET | static |
! 你只写了一条 route,表里却有两条——多出来那条 static 是 Flask 自己加的。
这就是「默认值也是决定」:它替你假设了要放 CSS、JS、图片的地方。
② 把这个默认关掉,看表少了什么
--------------------------------------------------------------------
| 规则 | 端点 |
|---|---|
| / | index2 |
只剩一条。传 static_folder=None,静态目录那条就没了——这个参数就是「收回它的一项决定」。
③ 真发一次请求
--------------------------------------------------------------------
GET / -> 状态码 200,正文 'hello, flask'
GET /nope -> 状态码 404(404 页面也是它给的)
| 响应头 | 值 |
|---|---|
| Content-Type | text/html; charset=utf-8 |
| Content-Length | 12 |
你没写这些头,是 Flask 补的。所以正文之外的每一样东西,它都在管。
④ 模板这一半,是另一个库在干活
--------------------------------------------------------------------
Flask 3.1.3,模板引擎是 Jinja2 3.1.6——两个独立项目,Flask 只是把它们接起来。
渲染一次:'张三 有 3 张图'
! 一句话:Flask 只规定「路由怎么配、模板怎么找、请求怎么读、响应怎么写」,其余(数据库、表单、
登录)全是可选插件。
这就是「微框架」的字面意思——核心小,功能靠装。
你只写了一条路由,表里却有两行。多出来那条 static 就是默认值。
static_folder=None 之后只剩一行——一个参数,收回一项决定。
响应头里的 Content-Type 和 Content-Length 也不是你写的,是它补的。
最后一节把 Jinja2 单拎出来,是为了说清:模板不是 Flask 自己实现的。
30 秒实验
- 把
Flask(__name__, static_folder=None)换成static_url_path="/assets", 看路由表里那条规则变成什么。 - 给
@app.route再加一个methods=["POST"],看路由表的方法列怎么变。 - 试着把返回值改成字典,看会发生什么——这一步能提前理解「响应」是什么。
常见误区
- 以为 Flask 自带服务器所以能上线:开发服务器只用于本机调试,并发一上来就撑不住, 真要上线得配 Gunicorn 或 Waitress。
- 以为「微框架」等于功能弱:功能都在插件里,装齐了不比大框架差,只是装的活要自己干。
- 不看路由表就调试 404:
flask routes一眼就能看出规则对不对,比翻代码快十倍。 - 把
Flask(__name__)写成别的字符串:第一个参数决定模板和静态文件从哪找,随手写个字面量, 模板就全找不到了。
自测三问(点开对答案)
1. 「微框架」的「微」指的是什么? —— 指核心小、不替你决定。数据库、表单、登录都不在里面,是装上去的插件。 2. 为什么说默认值也是一种决定? —— 因为不传 static_folder,它照样给你加一条 /static 路由,替你假设了静态文件放哪。 3. WSGI 里应用要回什么? —— 状态码、若干响应头、正文。Flask 把这些包装成 Response 对象,你返回字符串它也替你包。知道它不替你决定,接着就该问:那 Django 和 FastAPI 替你做多少?下一篇把四个框架摆在一起比。
- ☐ (本篇完,下一篇 0.2 Django、Bottle、FastAPI 与 Flask 的取舍)