浏览器请求:GET /hello
↓
Flask 匹配 @app.route('/hello') → 调用 hello() 函数
↓
hello() 中使用 render_template() 渲染 hello.html
↓
将 name="张三" 传入模板引擎 Jinja2
↓
Jinja2 渲染出 HTML: <h1>你好, 张三!</h1>
↓
Flask 把 HTML 打包成 Response 对象返回浏览器
↓
浏览器显示页面
render_template("模板名.html", 变量名1=值1, 变量名2=值2, ...)
模板中用 {{ 变量名 }} 就能访问到。
@app.route('/dashboard')
def dashboard():
stats = {"pv": 1000, "uv": 300}
return render_template('dashboard.html', stats=stats, username="admin")
templates/dashboard.html
<h2>欢迎, {{ username }}h2>
<p>页面浏览量:{{ stats.pv }}p>
<p>独立访客数:{{ stats.uv }}p>
{% if user %}
<p>欢迎 {{ user }}!p>
{% else %}
<p>请先登录p>
{% endif %}
{% if user %}
:检查 user 变量是否存在,如果存在,则显示欢迎消息,否则显示登录提示。
循环语句:
<ul>
{% for item in items %}
<li>{{ item }}li>
{% endfor %}
ul>
{% for item in items %}
:遍历 items 列表,并为每个项生成一个
元素。
自动转义:Jinja2 默认会对模板中的变量进行自动转义,防止 XSS 攻击。
{{ user_input }}
:用户输入的内容会被自动转义,以避免恶意脚本的注入。
比如
chart = "<script>alert('Hi')script>"
在模版中写
{{ chart }}
浏览器会看到:
<script>alert('Hi')</script>
因为 Flask 模板会自动进行 HTML 安全转义。
{{ chart | safe }} 是 Flask 模板中一个非常常见的写法,特别用于像 pyecharts、plotly 等图表库,把图表插入 HTML 页面的关键语句。
{{ chart | safe }} 意思是把 chart 的内容作为 HTML 原样输出(不再自动转义)
pyecharts 生成的图表是一个 HTML
from pyecharts.charts import Bar
from pyecharts import options as opts
bar = (
Bar()
.add_xaxis(["A", "B", "C"])
.add_yaxis("销量", [10, 20, 30])
)
chart = bar.render_embed()
在 Flask 中渲染:
return render_template("chart.html", chart=chart)
模板中这样写:
<div>
{{ chart | safe }}
div>
这样图表就会在页面中正常显示!
请不要对用户输入的数据使用 | safe,除非你已经对内容做过严格校验,否则可能产生 XSS 攻击漏洞。
模板继承允许你创建一个基础模板,然后在其他模板中继承和扩展这个基础模板,避免重复的 HTML 代码。
创建基础模板:在 templates 文件夹中创建一个基础模板 base.html。
templates/base.html 示例:
DOCTYPE html>
<html>
<head>
<title>{% block title %}My Website{% endblock %}title>
head>
<body>
<header>
<h1>My Websiteh1>
header>
<main>
{% block content %}{% endblock %}
main>
<footer>
<p>Footer contentp>
footer>
body>
html>
{% block title %}
{% endblock %}
和 {% block content %}
{% endblock %}
是定义的可替换区域。
创建子模板:在 templates 文件夹中创建一个子模板 index.html,继承 base.html。
templates/index.html 文件代码:
{% extends "base.html" %}
{% block title %}Home Page{% endblock %}
{% block content %}
<h2>Welcome to the Home Page!h2>
<p>Content goes here.p>
{% endblock %}
{% extends "base.html" %}
:继承基础模板。
{% block title %}
和 {% block content %}
:重写基础模板中的块内容。
templates/
├── base.html ← 通用母版模板
├── index.html ← 首页(继承 base)
└── login.html ← 登录页(继承 base)
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>{% block title %}默认标题{% endblock %}title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
head>
<body>
<header>
<h1>我的网站h1>
<nav>
<a href="{{ url_for('index') }}">首页a> |
<a href="{{ url_for('login') }}">登录a>
nav>
header>
<main>
{% block content %}{% endblock %}
main>
<footer>
<p>版权所有 © 2025p>
footer>
body>
html>
{% extends "base.html" %}
{% block title %}首页{% endblock %}
{% block content %}
<h2>欢迎访问首页h2>
<p>这里是首页内容p>
{% endblock %}
{% extends "base.html" %}
{% block title %}登录{% endblock %}
{% block content %}
<h2>登录页面h2>
<form method="post">
用户名: <input name="username"><br>
密码: <input name="password" type="password"><br>
<button type="submit">登录button>
form>
{% endblock %}
过滤器用于在模板中格式化和处理变量数据。
<p>{{ name|capitalize }}p>
<p>{{ price|round(2) }}p>
{{ name|capitalize }}
:将 name 变量的值首字母大写。
{{ price|round(2) }}
:将 price 变量的值四舍五入到小数点后两位。
宏是可重用的模板片段。模板包含允许你在一个模板中插入另一个模板的内容。
创建宏
templates/macros.html 代码文件:
实例
{% macro render_item(item) %}
<div>
<h3>{{ item.title }}h3>
<p>{{ item.description }}p>
div>
{% endmacro %}
使用宏:
templates/index.html 文件代码:
实例
{% from "macros.html" import render_item %}
<h1>Itemsh1>
{% for item in items %}
{{ render_item(item) }}
{% endfor %}
{% from "macros.html" import render_item %}
:导入宏。
{{ render_item(item) }}
:调用宏来渲染每个 item。
pip install flask-login
from flask_login import LoginManager
login_manager = LoginManager()
login_manager.init_app(app)
@login_manager.user_loader
def load_user(user_id):
return User.query.get(user_id)
from flask_login import UserMixin
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, nullable=False)
# 其他字段...
UserMixin
提供 is_authenticated、is_active、get_id() 等属性
将 UserMixin
添加到你的用户模型中后,它会自动为你提供实现 Flask-Login 所要求的接口。
from flask_login import login_user, logout_user
@app.route('/login', methods=['GET', 'POST'])
def login():
user = User.query.filter_by(username="admin").first()
login_user(user) # 登录
return redirect(url_for('index'))
@app.route('/logout')
def logout():
logout_user()
return redirect(url_for('index'))
from flask_login import current_user
<body>
{% if current_user.is_authenticated %}
<p>欢迎,{{ current_user.username }}!p>
<a href="{{ url_for('logout') }}">登出a>
{% else %}
<p>您尚未登录。p>
<a href="{{ url_for('login') }}">登录a>
{% endif %}
body>
{% if current_user.is_authenticated %}
:在使用 Flask-Login 时,current_user 是 Flask 提供的一个全局代理对象,代表当前登录的用户。
current_user.is_authenticated
是一个布尔值:
代码 | 说明 |
---|---|
current_user.is_authenticated |
判断当前用户是否登录 |
current_user.username |
访问当前登录用户属性 |
UserMixin |
提供用户模型默认认证属性 |