验证功能不写进主应用,单独做一个 auth 蓝图,注册时挂到 /auth 前缀下。 蓝图把注册、登入、登出三条地址收在一起,端点名统一带 auth. 前缀。 模板可以放进蓝图自己的目录,但应用目录里的同名模板会先被找到。
这一篇对应教材 3.1 与 3.2 到 3.2.5:目标架构、用 blueprint 登录验证功能、端点、模板、 页面内容、确认运作。 上一篇刚讲完蓝图的一般用法,这一篇把它落到一个真功能上。
一、这一章要加什么
第 2 章做出来的是「谁都能进的 CRUD 后台」。这一章给它加三道门:注册、登入、登出。 加完之后,CRUD 那几页必须登入才能进——这是教材 3.3.6 要做的事,也是这一章真正的目的。 所以这一章不是一个页面,是一整个子系统:三个端点、两张模板、一次模型改动。
先把它当子系统看,就不会写成「在首页上加个登入框」。
二、为什么单独做成蓝图
三道门和物件侦测功能没有关系,它只和用户表有关系。 混在 main 蓝图里,端点名就叫 register、login、logout, 将来要加「找回密码」「改密码」还得继续往里塞。 分开之后端点名统一带 auth. 前缀,一眼看出归属,删也能整块删。
判断标准是「这一块将来会不会被整块拿掉」——会被整块拿掉的,就该是蓝图。
三、三条地址长什么样
| 规则 | 端点 |
|---|---|
| /auth/ | auth.index |
| /auth/register | auth.register |
| /auth/login | auth.login |
| /auth/logout | auth.logout |
蓝图里的装饰器只写 /register,前缀在注册时给。这个分工上一篇讲过,这里是它的实际形状。
所以 url_for 里必须写全名:url_for("auth.login") 才是 /auth/login,
写 url_for("login") 会报 BuildError。
四、模板放哪:应用目录优先
蓝图可以带自己的模板目录:
Blueprint("auth", __name__, template_folder="templates") 会让 Flask 去 web/auth/templates 找。
实测两处都放一个同名的 probe.html,由蓝图渲染时用的是应用那一份。
顺序是:应用自己的 templates 排在前面,各蓝图的模板目录按注册顺序排在后面。
这个顺序很有用。想临时改某个页面的样子,不用动蓝图,往应用目录里放一份同名的就能盖住。 反过来也成立:蓝图里的那份只在应用目录没有同名文件时才生效, 所以「为什么我改的模板没反应」多半是撞了这个。
五、第一条要记住的规矩
有人没登入就想进受保护页面时,flask-login 会把他送去登录页。去哪个页面由 login_view 决定。 它写的是端点名,不是地址。写成 "/auth/login" 会在跳转那一刻抛 BuildError。 这个错误只在「有人被拦住」的时候才出现,手工点着测试很容易漏——因为你总是先登入再点页面。
六、动手 Demo:验证功能的蓝图:端点、模板与注册
这个 Demo 造一个带 auth 蓝图的项目:三条空壳端点、一份蓝图模板、一次前缀注册。 第三节故意在两处放同名模板,把 Flask 的查找顺序量出来。
# web/auth/views.py
from flask import Blueprint, render_template
bp = Blueprint("auth", __name__, template_folder="templates")
@bp.route("/register")
def register():
return render_template("auth/register.html")
# web/__init__.py
app.register_blueprint(auth_bp, url_prefix="/auth")
实跑输出:
--------------------------------------------------------------------
验证功能的蓝图:端点、模板与注册
--------------------------------------------------------------------
① 蓝图挂上 /auth 之后的路由表
--------------------------------------------------------------------
| 规则 | 端点 |
|---|---|
| / | main.index |
| /auth/ | auth.index |
| /auth/login | auth.login |
| /auth/logout | auth.logout |
| /auth/register | auth.register |
| /static/<path:filename> | static |
! auth 那四条地址全带 /auth 前缀,端点名全带 auth. 前缀。
前缀是注册时给的,蓝图里的装饰器只写 /register。
② url_for 怎么指到蓝图里的端点
--------------------------------------------------------------------
url_for('main.index') -> /
url_for('auth.index') -> /auth/
url_for('auth.register') -> /auth/register
url_for('auth.login') -> /auth/login
url_for('auth.logout') -> /auth/logout
! 写 url_for("register") 会报 BuildError——蓝图里的端点在应用看来是 auth.register。
③ 蓝图自己的模板目录
--------------------------------------------------------------------
两处都放了同名模板,GET /auth/probe -> 200
实际用的是:这是应用模板目录里的 probe.html
! 应用自己的 templates 排在前面,蓝图的模板目录排在后面。
所以同名时应用那一份会赢——想覆盖某个页面,往应用模板目录里放一份就行。
④ 确认运作
--------------------------------------------------------------------
GET / -> 200 <!doctype html> <html><body> <nav>
GET /auth/ -> 200 <!doctype html> <html><body> <nav>
GET /auth/register -> 200 注册页(下一篇填逻辑)
GET /auth/login -> 200 登入页(下一篇填逻辑)
GET /auth/logout -> 200 登出(下一篇填逻辑)
! 五个地址都通。这一篇只把路修到这儿,视图函数还是空壳,注册与登入的逻辑下一篇填。
路由表里 auth 那四条全带 /auth,端点名全带 auth.——前缀是注册时给的。 第三节那两个同名模板,实测用的是应用目录里的那一份,这就是「模板改了没反应」的常见原因。 第四节五个地址都返回 200,但视图还是空壳,逻辑下一篇才填。
30 秒实验
- 把 auth 蓝图的前缀改成 /u,看路由表与 url_for 各自怎么变。
- 删掉应用模板目录里的 probe.html,再访问 /auth/probe,看用的是哪一份。
- 想一想:如果把注册登入直接写进 main 蓝图,会有什么麻烦?
常见误区
- login_view 写地址:跳转那一刻报 BuildError,而且只在被拦住时才出现。
- url_for 不带蓝图名:BuildError,报错里还不会提示「你少写了前缀」。
- 模板目录写成绝对路径:Blueprint 的 template_folder 是相对蓝图包根目录的。
- 以为蓝图模板优先:同名时应用目录赢,改半天没反应就是因为这个。
自测三问(点开对答案)
1. 验证功能为什么单独做成一个蓝图? —— 它只和用户表有关,和业务无关,整块拿掉也不影响别的功能。 2. 蓝图里的 template_folder 相对谁? —— 相对蓝图所在的那个包,不是项目根目录。 3. 应用目录与蓝图目录有同名模板时谁优先? —— 应用目录,蓝图的排在后面。路修好了,下一篇请出 flask-login,让应用真的能认出谁在登入。
- ☐ (本篇完,下一篇 3.2 flask-login:让应用认识谁在登录)