一个网页请求要做三件事:拿到数据、决定用哪张页面、把数据填进页面。 MVT 就是把这三件事分给三个地方,改一头不用动另一头。 Flask 里的 V 是视图函数,它干的是 MVC 里 Controller 的活——名字别混。
从这一章起正式写代码。教材第 1 章是全书的地基,八篇覆盖 1.1 到 1.6 六节。 本篇先把分层讲清,后面七篇一样样落地:应用、配置、路由、模板、表单、消息、会话。
一、一次请求里发生了什么
浏览器请求 /books。
Flask 找到对应的视图函数,函数决定要哪些数据,再挑一张模板,把数据塞进去渲染成 HTML,
最后包成一个响应发回去。
这条路线上有三个可以独立更换的零件:数据从哪来、用哪张页面、页面长什么样。
分开的理由只有一个:它们的变更频率不一样。数据来源半年改一次,页面样式一周改三次,
混在一起就是天天互相踩。
二、Model:数据长什么样
Model 是最容易被误解的一层。它不是数据库表,是「数据在程序里长什么样」。
第 1 章还没有数据库,Model 就是一个普通类:有 title、price 两个属性。
第 2 章接上 SQLAlchemy 之后,同一个位置换成继承 db.Model 的类,
它的职责没变——只不过属性从内存搬到了表里。
所以现在别急着学 ORM,先把「Model 是一个带属性的对象」记住。后面换成数据库模型时, 模板那一层一个字都不用改。
三、View:取数据、挑模板
视图函数是唯一「知道全貌」的地方:它调 Model 拿数据,调 render_template 指定模板,
再把数据当参数传给模板。
三行代码,对应三件事:
@app.route("/books")
def books():
return render_template("show.html", books=BOOKS)
render_template 的第一个参数是模板文件名(相对模板目录),
后面的关键字参数一个个变成模板里的变量名。
函数返回的不是 HTML 字符串,是一个响应对象。render_template 只是把 HTML 装进去而已。
四、Template:只管长相
模板里可以写循环、判断、过滤器,但不该做业务判断。 「这本书打几折」是业务,「打折标签用什么颜色」才是模板的事。 判断标准很简单:这条逻辑换个页面还用得上吗?用得上就该在视图里算好再传进去。 教材在 1.1 节特别提醒:模板里塞太多逻辑,最后会变成另一种没人敢改的代码。
五、为什么叫 MVT 不叫 MVC
| MVC 里的角色 | 在 Flask 里落到哪 |
|---|---|
| Model | Model,没变 |
| View | Template,只管长相 |
| Controller | 视图函数,但它在 Flask 里被叫作 View |
所以 Flask 的 V 和 MVC 的 V 不是同一个东西。看到「视图」两个字,先想清楚说的是函数还是页面。 Django 也自称 MVT,用的是同一套命名。 这不是谁抄谁,是两边都不想让「控制器」这个词出现——Flask 里根本没有那个类。
六、动手 Demo:MVT:把一件事拆成三层
这一节的 Demo 用一个最小号的 MVT 把三层串起来:一个类当 Model,一个函数当 View, 一个模板文件当 Template。 然后往列表里塞一本书,看同一个模板怎么自动多出一行。
class Book:
def __init__(self, title, price):
self.title, self.price = title, price
BOOKS = [Book("Flask 实战", 89), Book("Python 入门", 69)]
@app.route("/books")
def books():
return render_template("show.html", books=BOOKS)
# show.html 里:
# <h1>{{ books|length }} 本书</h1>
# {% for b in books %}<li>{{ b.title }}</li>{% endfor %}
实跑输出:
--------------------------------------------------------------------
MVT:把一件事拆成三层
--------------------------------------------------------------------
① 三层各自管什么
--------------------------------------------------------------------
| 层 | 这次用的是什么 | 只管一件事 |
|---|---|---|
| Model | Book 类加 BOOKS 列表 | 数据长什么样 |
| View | books() 函数 | 取哪些数据、给哪个模板 |
| Template | show.html | 长什么样、怎么循环 |
! Flask 里的 V 其实是「视图函数」,它同时干了控制器那份活:既决定拿什么数据,也决定交给谁渲染。
② 真请求一次,看它拼出什么
--------------------------------------------------------------------
GET /books -> 200,Content-Type text/html; charset=utf-8
<h1>2 本书</h1>
<ul>
<li>Flask 实战 89</li>
<li>Python 入门 69</li>
</ul>
2 是 len(BOOKS) 算出来的,书名列是 for 循环铺出来的——模板里没有一条写死的书名。
③ 换数据不用改模板
--------------------------------------------------------------------
往列表里再塞一本,同一个请求再来一次:
<h1>3 本书</h1>
! 这就是 MVT 的分工价值:数据和长相分开,改一头不用动另一头。
④ 名字千万别混
--------------------------------------------------------------------
| MVC 里的角色 | 在 Flask 里落到哪 |
|---|---|
| MVC 的 C(Controller) | MVT 里没有这个位置 |
| MVC 的 V(View) | MVT 的 T(Template),只管长相 |
| —— | MVT 的 V(View)干的是 MVC 里 Controller 的活 |
教材 1.1 节专门解释过这件事:Django 和 Flask 都叫自己 MVT,但那个 V 指的是「视图函数」,
不是「视图模板」。
所以看到「视图」两个字,先想清楚说的是函数还是页面——这是新手第一个混淆点。
「2 本书」那个 2 是模板里用 length 过滤器算出来的,不是写死的。 塞进第三本书后重发同一个请求,标题自动变成 3——模板一个字没改。 最后那张表专门理清 MVC 与 MVT 的错位,这是读 Django 文档时最容易绕晕的一段。
30 秒实验
- 把
BOOKS清空,看for循环一个都不进时页面渲染成什么样。 - 在模板里加一个
{% if books %},写出「还没有书」的空状态。 - 想一想:如果「按价格排序」这件事,该放在视图里还是模板里?为什么?
常见误区
- 在模板里查数据库:模板里能跑代码,但不该碰数据层,否则换数据源时每张页面都要改。
- 把 HTML 拼在视图函数里:等于把 Template 那层删了,改一次样式就要动 Python 代码。
- 以为 MVT 的 V 是页面:它是函数,真正管长相的是 T。
- Model 里塞业务逻辑:「打几折」这类规则放进模型,换个场景(比如批发)就复用不了。
自测三问(点开对答案)
1. 视图函数在这三层里为什么特殊? —— 只有它同时知道另外两层:既调 Model 取数据,又指定模板并传参。 2. Flask 的 View 和 MVC 的 View 差在哪? —— MVC 的 View 是页面,Flask 的 View 是函数,干的是 Controller 的活。 3. 模板里该不该写业务判断? —— 不该。判断「换张页面还用不用」——复用不了的就该在视图里算好。分层讲完了,下一篇动手写第一个能跑的应用,顺带把除错模式到底多给了什么量出来。
- ☐ (本篇完,下一篇 1.2 最小可运行应用与除错模式)