现在位置: 首页 > Flask 教程 > 正文

蓝图(Blueprint)

当应用只有一个文件时,所有路由写在一起还没问题。

但随着功能增长——用户模块、文章模块、管理后台——代码会变得难以维护。

Blueprint(蓝图)就是 Flask 提供的模块化解决方案,让你将应用拆分为独立的功能单元。


为什么需要蓝图

蓝图解决的核心问题:

  • 代码组织:将相关的路由、模板、静态文件归为一组
  • 复用性:同一个蓝图可以注册到不同应用,或注册多次(加不同前缀)
  • 团队协作:不同开发者负责不同的蓝图模块,减少冲突

Flask Blueprint 蓝图模块架构


创建蓝图

蓝图使用 Blueprint 类创建,用法和 Flask 非常相似——同样的路由装饰器、同样的视图函数写法:

实例

# 文件路径:auth.py(用户认证蓝图)
from flask import Blueprint, render_template, request, session, redirect, url_for

# 创建蓝图实例
# 第一个参数 "auth" 是蓝图的名称(用于 url_for 引用)
# __name__ 告诉蓝图从哪里查找模板和静态文件
bp = Blueprint("auth", __name__)

# 蓝图中使用 bp.route,而不是 app.route
@bp.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form.get("username", "")
        if username:
            session["username"] = username
            return redirect(url_for("blog.index"))
    return render_template("auth/login.html")

@bp.route("/register", methods=["GET", "POST"])
def register():
    if request.method == "POST":
        username = request.form.get("username", "")
        if username:
            return redirect(url_for("auth.login"))
    return render_template("auth/register.html")

@bp.route("/logout")
def logout():
    session.clear()
    return redirect(url_for("blog.index"))

实例

# 文件路径:blog.py(博客蓝图)
from flask import Blueprint, render_template

bp = Blueprint("blog", __name__)

@bp.route("/")
def index():
    # 模拟文章数据
    posts = [
        {"title": "Flask 入门", "author": "runoob"},
        {"title": "Blueprint 详解", "author": "RUNOOB"},
    ]
    return render_template("blog/index.html", posts=posts)

@bp.route("/create", methods=["GET", "POST"])
def create():
    return render_template("blog/create.html")

注册蓝图

蓝图创建后不会自动生效,需要在应用中注册:

实例

# 文件路径:app.py
from flask import Flask
from auth import bp as auth_bp
from blog import bp as blog_bp

app = Flask(__name__)
app.secret_key = "dev-secret-key"

# 注册蓝图到应用
# url_prefix:为蓝图中所有路由添加统一的前缀
app.register_blueprint(auth_bp, url_prefix="/auth")
# auth 蓝图的路由变成:/auth/login, /auth/logout, /auth/register

app.register_blueprint(blog_bp)
# blog 蓝图不带前缀,路由保持:/、/create

注册后的完整路由表:

URL 蓝图 endpoint(url_for 使用)
/ blog blog.index
/create blog blog.create
/auth/login auth auth.login
/auth/register auth auth.register
/auth/logout auth auth.logout

注册蓝图后,url_for() 中的 endpoint 需要加上蓝图名前缀,例如 url_for("auth.login") 而非 url_for("login")


url_for 在蓝图中的特殊用法

同一个蓝图中,可以使用点号开头的相对引用:

实例

# 在 auth.py 内部,使用相对引用更简洁
@bp.route("/")
def index():
    # 同蓝图内的相对引用(以 . 开头)
    login_url = url_for(".login")       # 等价于 url_for("auth.login")
    register_url = url_for(".register") # 等价于 url_for("auth.register")
    # 跨蓝图引用需要使用完整 endpoint
    blog_url = url_for("blog.index")    # 引用 blog 蓝图的 index

蓝图的模板和静态文件

蓝图也可以拥有自己的模板和静态文件。

当蓝图有自己的模板文件夹时,Flask 会先在应用的 templates 中查找,找不到再去蓝图指定的文件夹查找。

实例

# 创建带有独立模板文件夹的蓝图
bp = Blueprint("auth", __name__, template_folder="templates")

# 蓝图特有的模板组织
# 路径:auth/templates/auth/login.html
# 渲染时:render_template("auth/login.html")
# 这样组织可以避免文件名冲突(login.html 在 auth 和 blog 中可能是不同的)

推荐的蓝图目录结构:

myflaskapp/
├── app.py              # 应用入口,注册蓝图
├── auth.py             # 认证蓝图
├── blog.py             # 博客蓝图
├── templates/          # 应用级模板(公共 base.html 等)
│   └── base.html
├── static/             # 应用级静态文件
│   └── style.css
├── auth/               # auth 蓝图的独立资源(可选)
│   └── templates/
│       └── auth/
│           ├── login.html
│           └── register.html
└── blog/               # blog 蓝图的独立资源(可选)
    └── templates/
        └── blog/
            ├── index.html
            └── create.html

最简单的做法是不给蓝图配置独立模板文件夹,模板统一放在项目根目录的 templates/ 下,用子目录区分:templates/auth/login.htmltemplates/blog/index.html


工厂模式——create_app

随着蓝图变多,直接在模块顶层创建 app = Flask(__name__) 会遇到循环导入问题。

工厂模式(Factory Pattern)是解决这个问题的最佳实践:

实例

# 文件路径:app.py
from flask import Flask

def create_app():
    """应用工厂函数——返回配置好的 Flask 实例"""
    app = Flask(__name__)
    app.secret_key = "dev-secret-key"

    # 加载配置
    app.config.from_pyfile("config.py", silent=True)

    # 在函数内部导入蓝图,避免循环引用
    from auth import bp as auth_bp
    from blog import bp as blog_bp

    # 注册蓝图
    app.register_blueprint(auth_bp, url_prefix="/auth")
    app.register_blueprint(blog_bp)

    return app

工厂模式的优势:

  • 消除循环导入:蓝图可以导入 appapp 也可以导入蓝图,因为导入发生在函数内部
  • 支持测试:可以创建不同配置的 app 实例进行测试
  • 多实例部署:同一份代码可以创建多个独立的 app 实例

使用工厂模式后,需要通过 flask --app "app:create_app()" run 启动:

$ flask --app "app:create_app()" run

蓝图中的其他功能

蓝图也支持 before_request、errorhandler 等装饰器,用法和 app 一致:

实例

@bp.before_request
def check_login():
    """在 auth 蓝图的所有请求前检查登录状态"""
    if request.endpoint not in ("auth.login", "auth.register"):
        if "username" not in session:
            return redirect(url_for("auth.login"))

@bp.errorhandler(404)
def not_found(error):
    """auth 蓝图专用的 404 错误页面"""
    return render_template("auth/404.html"), 404

蓝图的 before_request 只影响该蓝图内的路由。如果你需要在所有请求前执行逻辑,应该用 app.before_request