17.Flask模板

1 模板介绍

思考:网站如何向客户端返回一个漂亮的页面呢?

  • 提示
    • 漂亮的页面需要 htmlcssjs
    • 可以把这一堆字段串全都写到视图中,作为 HttpResponse() 的参数,响应给客户端。
from flask import Flask

app = Flask(__name__)


@app.route("/")
def index():
    return "<h1>Hello</h1><p style='color:red'>这个是内容!!</p>"


if __name__ == '__main__':
    app.run(debug=True)

问题
- 视图部分代码臃肿,耦合度高。
> - 这样定义的字符串是不会出任何效果和错误的。 > - 效果无法及时查看。有错也不容易及时发现。
解决问题
模板 Template:
> - `MVT` 设计模式中的 `T`,`Template` > > **M** 全拼为 Model,与 MVC 中的 M 功能相同,负责和数据库交互,进行数据处理。 > > **V** 全拼为 View,与 MVC 中的 C 功能相同,接收请求,进行业务处理,返回应答。 > > **T** 全拼为 Template,与 MVC 中的 V 功能相同,负责封装构造要返回的 html。

2 模板的使用

在 Flask 中,配套的模板是 Jinja2,Jinja2 的作者也是 Flask 的作者。这个模板非常的强大,并且执行效率高。

使用步骤

  • 创建模板
    • 应用同级目录下创建模板文件夹 templates。文件夹名称固定写法。
    • templates 文件夹下,创建应用同名文件夹。例,book
    • 应用同名文件夹下创建网页模板文件。例:index.html
  • 设置模板查找路径
    • 如果想要修改模板的目录,可以设置 template_folder 参数。
    • 在 Flask 中指定存放模板的文件夹。
    • Flask(__name__, template_folder='template')
  • 模板处理数据

from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    # 模板默认查找的目录是 templates,
    # 如果想要修改模板的目录,可以设置 template_folder 参数
    # 通过在 Flask 中指定存放模板的文件夹
    # Flask(__name__, template_folder='template')
    return render_template("index.html")


if __name__ == '__main__':
    app.run(debug=True)

3 模板——传参

在使用 render_template 渲染模版的时候,可以传递关键字参数(命名参数)。

from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    # 传递一个参数
    return render_template("index.html", info="Flask 模板传参")


@app.route("/home/")
def home():
    # 传递多个参数
    return render_template("index.html",
                           arg="Python 中的 Flask 模板传参",
                           info="Flask 模板传参")
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>模板传参</title>
</head>

<body>
    <h1>模板传参</h1>
    <p>这是关于 {{ info }} 的内容</p>
    <hr>
    <p>这是关于 {{ arg }} 的内容</p>
    <hr>
    <p>这是关于 {{ address }} 的内容</p>
</body>

</html>

从结果可以看出,如果模板没有获取到参数,模板也不会报错,只是读取参数的地方什么都没有。

小技巧
如果你的参数项过多,那么可以将所有的参数放到一个字典中,然后在传这个字典参数的时候,使用两个星号,将字典打散成关键字参数(也叫命名参数)。
@app.route("/index/")
def index1():
    # 传递多个参数
    context = {
        "uname": "小龙女",
        "age": 18,
        "job": "掌门",
        'tudi': {
            'uname': '杨过',
            'height': '180cm'
        }
    }

    # 通过系列解包进行传参 **context
    return render_template("index.html", **context)

字典数据获取方式是:{{ tudi['uname'] }}{{ tudi.get("uname") }} 或者 {{ tudi.uname }}

<p>师傅:{{ uname }}==={{ age }}==={{ job }}</p>
<hr>
<p>徒弟:{{ tudi.uname }}==={{ tudi.height }}</p>

在模板中获取字典中没有的属性时:

<p>师傅:{{ uname }}==={{ age }}==={{ job }}</p>
<hr>
<p>徒弟:{{ tudi.uname }}==={{ tudi.height }}==={{ tudi["age"] }}==={{ tudi.get("score") }}==={{ tudi.address }}</p>

可以看出,使用 tudi["age"]tudi.address 两种方式获取字典中不存在的属性时不会报错,且什么也不会返回;而 tudi.get("score") 会返回 None

4 模板使用 url_for 函数

我们在 html 的一个页面中,可能有很多的链接都会指向同一个 url,比如点击 logo 图片跳转首页,点击页面的标题也跳转首页。那如果我们修改了主页的 url 就需要去 html 代码中一个一个的修改 a 标签,太麻烦了,能不能像视图函数中一样使用 url_for 反向定位 url 呢?

答案是可以的。模版中也可以使用 url_for,和后台视图函数中的 url_for 使用起来基本是一模一样的。

提示
在模板中使用函数,需要在函数 左右两边加上 2 个 `{}`。
> > 例如:`{{ url_for(func) }}`。
from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/home/")
def home():
    return "home"


@app.route("/info/<int:id>/")
def info(id):
    return "info"


if __name__ == '__main__':
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板中使用url_for函数</title>
</head>
<body>
    <h1>模板中使用url_for函数</h1>
    <hr>
    <p>index 页面 URL:{{ url_for("index") }}</p>
    <hr>
    <p>home 页面 URL:{{ url_for("home") }}</p>
    <hr>
    <p>info 页面 URL:{{ url_for("info", id=1101) }}</p>
    <hr>
    <p>home 页面 URL:{{ url_for("home", id=1101) }}</p>
    <hr>
    <p>info 页面 URL:{{ url_for("info", id=1101, addr="bj") }}</p>
    <hr>
    <a href="/home/">访问 home 页面</a>
    <hr>
    <a href="{{ url_for('home') }}">访问 home 页面</a>
</body>
</html>

注意
无论是**路径参数**还是**查询式参数**都可以直接传递。

5 过滤器介绍

有时候我们想要在模版中对一些变量进行处理,那么就必须需要类似于 Python 中的函数一样,可以将这个值传到函数中,然后做一些操作。

在模版中,过滤器相当于是一个函数,把当前的变量传入到过滤器中,然后过滤器根据自己的功能,再返回相应的值,之后再将结果渲染到页面中。

例如:将传递过来的浮点数转换成整数展示。

from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    return render_template("index.html", param=10.5)


if __name__ == '__main__':
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>过滤器的使用</title>
</head>

<body>
    <h1>过滤器的使用</h1>
    <p>过滤前的数据是:{{ param }}</p>
    <p>过滤后的数据是:{{ param | int }}</p>
</body>

</html>

6 Jinja 模板自带过滤器

过滤器是通过管道符号 | 使用的,例如:{{ name | length }} 将返回 name 的长度。

过滤器相当于是一个函数,把当前的变量传入到过滤器中,然后过滤器根据自己的功能,再返回相应的值,之后再将结果渲染到页面中。

Jinja2 中内置了许多过滤器:https://jinja.palletsprojects.com/en/3.0.x/templates/#filters

过滤器名 解释 举例 结果
abs(value) 返回一个数值的绝对值。 {{ -1 | abs }} 1
int(value) 将值转换为 int 类型。 {{ 10.5 | int }} 10
float(value) 将值转换为 float 类型。 {{ 10 | float }} 10.0
string(value) 将变量转换成字符串。 {{ 20 | string }} 20
default(value, default_value, boolean=false) 如果当前变量没有值,则会使用参数中的值来代替。

如果想使用 Python 的形式判断是否为 false,则可以传递 boolean=true。也可以使用 or 来替换。
{{ name | default('xiaotuo') }} 如果没有传递 name 的值,则显示 xiaotuo,传递了则显示 name 的值。
safe(value) 如果开启了全局转义,那么 safe 过滤器会将变量关掉转义。模板默认开启转义。 {{ content_html | safe }}
escape(value)e 转义字符,会将 >< 等符号转义成 HTML 中的符号。模板默认是开启的。开启转义后不会将传过来的参数中的 <a> 等字段解释为标签,而是当作普通字符串。 {{ content | escape }}{{ content | e }}
first(value) 返回一个序列的第一个元素。 {{ names | first }} names = ["xln", "zm", "zzr", "hr"]

显示:xln
last(value) 返回一个序列的最后一个元素。 {{ names | last }} names = ["xln", "zm", "zzr", "hr"]

显示:hr
length(value) 返回一个序列或者字典的长度。 {{ names | length }} names = ["xln", "zm", "zzr", "hr"]

显示:4
join(value, d='+') 将一个序列用 d 这个参数的值拼接成字符串。 {{ names | join("+") }} names = ["xln", "zm", "zzr", "hr"]

显示:xln+zm+zzr+hr
format(value,*arags,**kwargs) 格式化字符串。 {{ "%s--%s" | format("小龙女", "掌门") }} 显示:小龙女--掌门
lower(value) 将字符串转换为小写。 {{ str | lower }} str= Python

显示:python
upper(value) 将字符串转换为大写。 {{ str | upper }} str= Python

显示:PYTHON
replace(value, old, new) 替换将 old 替换为 new 的字符串。 {{ "Hello World" | replace("Hello", "Hi") }} Hi World
truncate(value, length=255, killwords=False) 截取 length 长度的字符串。
striptags(value) 删除字符串中所有的 HTML 标签,如果出现多个空格,将替换成一个空格。
trim 截取字符串前面和后面的空白字符。 {{ "   Hello World   " | wordcount }} Hello World
wordcount(s) 计算一个长字符串中单词的个数。 {{ "Hello World" | wordcount }} 2

6.1 defalut 过滤器

from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    return render_template("index.html",
                           name="小龙女",
                           job="")


if __name__ == '__main__':
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>default过滤器的使用</title>
</head>

<body>
    <h1>default 过滤器的使用</h1>
    <p>传递 name 参数:{{ name }}</p>
    <p>未传递 param 参数:{{ param | default("张敏") }}</p>
    <p>判断传递的参数在 Python 中是否为 false:{{ job | default("掌门", boolean=True) }}</p>
    <p>判断传递的参数在 Python 中是否为 false:{{ job or "掌门" }}</p>
</body>

</html>

6.2 转义字符

from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    info = "<b>我是传递的数据</b>"
    return render_template("index.html", info=info)


if __name__ == '__main__':
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>转义字符过滤器的使用</title>
</head>

<body>
    <h1>转义字符过滤器的使用</h1>
    <p>转义前的参数:{{ info }}</p>
    <p>转义后的参数:{{ info | safe }}</p>

    <!-- 当我们想要控制转义的字符串非常多时,可以将这些字符串放在一个代码块中统一管理 -->
    <p>
        代码块中的内容全部 开启/关闭 转义:
        <!-- false 代表不再转义特殊字符 / true 转义特殊字符 -->
        {% autoescape True %}
        {{ info }}
        {{ info }}
        {{ info }}
        {{ info }}
        {% endautoescape %}
    </p>
</body>

</html>

小提示
jinja2 模板默认全局开启了自动转义功能。
> - `safe` 过滤器:可以关闭一个字符串的自动转义。 > - `escape` 过滤器:对某一个字符串进行转义。 > - `autoescape` 标签,可以对他包含的代码块关闭或开启自动转义。 > > ```html > {% autoescape true/false %} > 代码块 > {% endautoescape %} > ``` >

6.3 其它过滤器

from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    info = "<b>我是传递的数据</b>"
    return render_template("index.html", info=info)


if __name__ == '__main__':
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>其它过滤器的使用</title>
</head>

<body>
    <h1>其它过滤器的使用</h1>
    <!-- 为了方便,没有传递数据,实际都应该从后台传递过来 -->
    <p>绝对值:{{ -6 | abs }}</p>
    <p>整数:{{ 10.5 | int }}</p>
    <p>浮点数:{{ 10 | float }}</p>
    <p>字符串:{{ 10 | string }}</p>
    <br>

    <p>格式化字符串:{{ "%s--%s" | format("小龙女", "掌门") }}</p>
    <p>字符串长度:{{ "Hello World" | length }}</p>
    <p>字符串转为大写:{{ "Python" | upper }}</p>
    <p>字符串转为小写:{{ "Python" | lower }}</p>
    <p>字符串替换:{{ "Hello World" | replace("Hello", "Hi") }}</p>
    <p>单词数量:{{ "Hello World" | wordcount }}</p>
    <p>去掉字符串开头和结尾的空白:{{ "   Hello World   " | trim }}</p>
    <br>

    <p>序列中第一个元素:{{ ["小龙女", "赵敏", "周芷若"] | first }}</p>
    <p>序列中最后一个元素:{{ ["小龙女", "赵敏", "周芷若"] | last }}</p>
    <p>序列长度:{{ ["小龙女", "赵敏", "周芷若"] | length }}</p>
    <p>序列拼接为字符串:{{ ["小龙女", "赵敏", "周芷若"] | join("+") }}</p>
</body>

</html>

7 自定义过滤器

只有当系统提供的过滤器不符合需求后,才须要自定义过滤器。过滤器本质上就是一个函数。

如果在模版中调用这个过滤器,那么就会将这个变量的值作为第一个参数传给过滤器这个函数,然后函数的返回值会作为这个过滤器的返回值。

需要使用到一个装饰器:

@app.template_filter('过滤器名称')

7.1 自定义数据替换过滤器

例如:将传递过来的数组按格式拼接为字符串输出。

from flask import Flask, render_template

app = Flask(__name__)


@app.template_filter("to_str")
def to_str(value: list) -> str:
    value_str = "===".join(value)
    return value_str


@app.route("/")
def index():
    info = ["小龙女", "赵敏", "周芷若"]
    return render_template("index.html", info=info)


if __name__ == '__main__':
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>自定义内容过滤器</title>
</head>

<body>
    <h1>自定义内容过滤器</h1>
    <!-- 将传递过来的数组按格式拼接为字符串输出 -->
    <p>过滤前的数据:{{ info }}</p>
    <p>过滤后的数据:{{ info | to_str }}</p>
</body>

</html>

7.2 自定义时间过滤器

例如:显示发布新闻与现在的时间间隔。

from datetime import datetime

from flask import Flask, render_template

app = Flask(__name__)


# 年 月 日  时 分 秒
# 数据库中存放的数据是 2030/01/01 00:00:00
# 现在时间是         2030/01/01 01:30:00
@app.template_filter("handler_time")
def handler_time(time: datetime) -> str:
    """
        time距离现在的时间间隔
            1. 如果时间间隔小于 1 分钟以内,那么就显示“刚刚”
            2. 如果是大于 1 分钟小于 1 小时,那么就显示“xx分钟前”
            3. 如果是大于 1 小时小于 24 小时,那么就显示“xx小时前”
            4. 如果是大于 24 小时小于 30 天以内,那么就显示“xx天前”
            5. 否则就是显示具体的时间 2030-10-20 16:15:10
    """
    minute = 60
    hour = 60 * minute
    day = 24 * hour
    # 获取现在的时间
    now_time = datetime.now()
    # 将相差的时间转为秒
    temp_stamp = (now_time - time).total_seconds()

    if temp_stamp < 60:
        return "刚刚"
    elif temp_stamp / minute < 60:
        return f"{int(temp_stamp / minute)}分钟之前"
    elif temp_stamp / hour < 24:
        return f"{int(temp_stamp / hour)}小时之前"
    elif temp_stamp / day < 30:
        return f"{int(temp_stamp / day)}天之前"
    else:
        return f"{time}"


@app.route("/")
def index():
    now_time = datetime(2025, 9, 4, 9, 7, 10)
    return render_template("index.html", now_time=now_time)


if __name__ == '__main__':
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>自定义时间过滤器</title>
</head>

<body>
    <h1>自定义时间过滤器</h1>
    <p>距离现在:{{ now_time | handler_time }}</p>
</body>

</html>

8 流程控制——选择结构

所有的控制语句都是放在 {% … %} 中,并且有一个语句 {% endxxx %} 来进行结束!

if:if 语句和 Python 中的类似,可以使用 >,<,<=,>=,==,!= 来进行判断,也可以通过 and, or, not, () 来进行逻辑合并操作。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <title>if语句的使用</title>
</head>

<body>
    <h1>if语句的使用</h1>

    <h3>单分支</h3>
    {% if uname == "xln" %}
    <p>小龙女</p>
    {% endif %}

    <hr />
    <h3>双分支</h3>
    {% if uname == "xln" %}
    <p>小龙女</p>
    {% else %}
    <p>{{ uname }}</p>
    {% endif %}

    <hr />
    <h3>多分支</h3>
    {% if uname == "xln" %}
    <p>小龙女</p>
    {% elif uname == "zm" %}
    <p>赵敏</p>
    {% else %}
    <p>{{ uname }}</p>
    {% endif %}
</body>

</html>
from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    uname = "zm"
    return render_template("index.html", uname=uname)


if __name__ == '__main__':
    app.run(debug=True)
注意
`if` 条件判断语句必须放在 `{% if statement %}` 中间,并且还必须有结束的标签 `{% endif %}`。

9 流程控制——选择练习

from flask import Flask, render_template

app = Flask(__name__)


@app.route("/login/")
def login():
    # 注册用户
    user = "小龙女"
    return render_template("index.html", user=user)


@app.route("/")
def index():
    return render_template("index.html")


if __name__ == '__main__':
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8"/>
    <title>if语句的练习</title>
</head>

<body>
    <span>中国大陆</span>&nbsp;&nbsp;&nbsp;
    {% if not user %}
        <a href="{{ url_for('login') }}">亲,请登录</a>&nbsp;&nbsp;&nbsp;
        <span>免费注册</span>&nbsp;&nbsp;&nbsp;
    {% else %}
        <span> {{ user }} </span>
    {% endif %}
    <span>手机逛淘宝</span>
</body>

</html>

10 流程控制——循环结构

for…in… for 循环可以遍历任何一个序列包括列表、字典、元组。并且可以进行反向遍历,以下将用几个例子进行解释:

列表

<ul>
	{% for user in users %}
		<li>{{ user }}</li>
	{% endfor %}
</ul>

遍历字典

<tr>
	{% for key in person.keys() %}
		<td>{{ key }}</td>
	{% endfor %}
</tr>

<tr>
	{% for val in person.values() %}
		<td>{{ val }}</td>
	{% endfor %}
</tr>

<tr>
	{% for item in person.items() %}
		<td>{{ item }}</td>
	{% endfor %}
</tr>

<tr>
	{% for key,value in person.items() %}
		<td>{{ value }}</td>
	{% endfor %}
</tr>

如果序列中没有值的时候,进入 else

反向遍历用过滤器 reverse

<ul>
	{% for user in users | reverse %}
		<li>{{ user }}</li>
	{% else %}
		<li>没有任何用户</li>
	{% endfor %}
</ul>

并且 Jinja 中的 for 循环还包含以下变量,可以用来获取当前的遍历状态:

变量 描述
loop.index 当前迭代的索引(从 1 开始)
loop.index0 当前迭代的索引(从 0 开始)
loop.first 是否是第一次迭代,返回 True 或 False
loop.last 是否是最后一次迭代,返回 True 或 False
loop.length 7 序列的长度
总结
在 jinja2 中的 for 循环,跟 python 中的 for 循环基本上是一模一样的,也是 `for…in…` 的形式,并且也可以遍历所有的序列以及迭代器。唯一不同的是,jinja2 中的 for 循环没有 `break` 和 `continue` 语句。

案例:

from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    items = ["小龙女", "赵敏", "周芷若", "黄蓉", "杨过", "郭靖", "张无忌"]
    person = {"uname": "张三", "age": 18, "height": 180, "nick": "法外狂徒"}

    return render_template("index.html", items=items, person=person)


if __name__ == '__main__':
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>流程控制——循环结构</title>
</head>

<body>
    <h1>流程控制——循环结构</h1>
    <h3>列表数据</h3>
    <p>{{ items }}</p>

    <h4>遍历列表</h4>
    <p>
        {% for item in items %}
            {{ item }}<br>
        {% endfor %}
    </p>

    <p>
        {% for item in items | reverse %}
            {{ item }}<br>
        {% endfor %}
    </p>

    <hr>
    <h3>字典数据</h3>
    <p>
        {{ person }}
        <br>
        {{ person.uname }}
    </p>

    <h4>字典遍历</h4>
    <p>
        {% for k in person %}
            {{ k }}<br>
        {% endfor %}
    </p>

    <p>
        {% for v in person.values() %}
            {{ v }}<br>
        {% endfor %}
    </p>

    <p>
        {% for i in person.items() %}
            {{ i }}<br>
        {% endfor %}
    </p>

    <p>
        {% for item in person.items() %}
            {{ item }} === {{ loop.index }} === {{ loop.index0 }} === {{ loop.first }} === {{ loop.last }} ===
            {{ loop.length }}<br>
        {% endfor %}
    </p>
</body>

</html>

11 流程控制——循环练习

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>for语句的练习</title>
    <style>
        td {
            padding: 10px;
            border: 1px solid black;
        }
    </style>
</head>

<body>
    <h1>循环练习-99乘法表</h1>
    <table>
        {% for row in range(1, 10) %}
            <tr>
                {% for col in range(1, row + 1) %}
                    <td>{{ col }} * {{ row }} = {{ col * row }}</td>
                {% endfor %}
            </tr>
        {% endfor %}
    </table>
</body>

</html>

12 宏的使用

模板中的宏跟 Python 中的函数类似,可以传递参数,但是不能有返回值

可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个变量。

12.1 定义宏

<!-- 定义宏 -->
{% macro inp(type, name="", value="") %}
    <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}

12.2 使用宏

不使用宏:

<table>
	<tr>
		<td>用户名:</td>
		<td><input type="text" name="uname"></td>
	</tr>
	<tr>
		<td>密码:</td>
		<td><input type="password" name="pwd"></td>
	</tr>
	<tr style="text-align: center">
		<td colspan="2"><input type="submit" value="登录"></td>
	</tr>
</table>

使用宏:

<table>
	<tr>
		<td>用户名:</td>
		<td>{{ inp("text", "uname") }}</td>
	</tr>
	<tr>
		<td>密码:</td>
		<td>{{ inp("password", "pwd") }}</td>
	</tr>
	<tr style="text-align: center">
		<td colspan="2">{{ inp("submit", value="登录") }}</td>
	</tr>
</table>

12.3 访问结果

提示
实际开发中,不会把宏在一个页面内定义 并直接使用般把宏定义放到一个专门的文件夹中,方便进行统一管理之后,哪一个页面需要使用某个宏,需要导入宏才能使用。

12.4 导入宏方式

  1. from "宏文件的路径" import 宏的名字 [as xxx]
{% from "users/users.html" import input as inp %}
  1. import "宏文件的路径" as xxx [with context]
{% import "users/users.html" as usr %}
注意
1. 宏文件路径,不要以相对路径去寻找,都要以 `templates` 作为绝对路径去找。
> 2. 如果想要在导入宏的时候,就把当前模版的一些参数传给宏所在的模版,那么就应该在导入的时候使用 `with context`。 > > ```html > {% import "users/users.html" as usr with context %} > ```
from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    return render_template("index.html")


if __name__ == '__main__':
    app.run(debug=True)

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>宏的引入</title>
</head>

<!--
    Python 中引入文件的两种方式:
        import file
        from file import func_name
-->

{% import 'macros/common.html' as mc %}
{% from 'macros/common.html' import inp %}

<body>
    <h1>宏的引入</h1>
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="uname"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="pwd"></td>
        </tr>
        <tr style="text-align: center">
            <td colspan="2"><input type="submit" value="登录"></td>
        </tr>
    </table>

    <hr>

    <table>
        <tr>
            <td>用户名:</td>
            <td>{{ mc.inp(name="uname") }}</td>
        </tr>
        <tr>
            <td>密码:</td>
            <td>{{ mc.inp("password", "pwd") }}</td>
        </tr>
        <tr style="text-align: center">
            <td colspan="2">{{ mc.inp("submit", value="登录") }}</td>
        </tr>
    </table>

    <hr>

    <table>
        <tr>
            <td>用户名:</td>
            <td>{{ inp(name="uname") }}</td>
        </tr>
        <tr>
            <td>密码:</td>
            <td>{{ inp("password", "pwd") }}</td>
        </tr>
        <tr style="text-align: center">
            <td colspan="2">{{ inp("submit", value="登录") }}</td>
        </tr>
    </table>
</body>

</html>

common.html

{% macro inp(type="text", name="", value="") %}
    <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}

如果我们想从后端传递一些数据,在模板中可以使用吗?

可以,比如后台传递了 nick="小龙女",将其作为用户名的默认值。实现如下。

<!-- html 代码中 -->
<tr>
	<td>用户名:</td>
	<td><input type="text" name="uname" value={{ nick }}></td>
</tr>

<!-- 使用宏 -->
<tr>
	<td>用户名:</td>
	<!-- 直接写变量名 -->
	<td>{{ inp(name="uname", value=nick) }}</td>
</tr>
注意
在宏中使用后台传递的参数时,不需要用两个大括号包裹变量。
> > ```html > > {{ inp(name="uname", value=nick) }} > > > {{ inp(name="uname", value={{ nick }}) }} > ```

完整示例:

from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    nick = "小龙女"
    return render_template("index.html", nick=nick)


if __name__ == '__main__':
    app.run(debug=True)

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>宏的引入</title>
</head>

<!--
    Python 中引入文件的两种方式:
        import file
        from file import func_name
-->

{% import 'macros/common.html' as mc %}
{% from 'macros/common.html' import inp %}

<body>
    <h1>宏的引入</h1>
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="uname" value="{{ nick }}"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="pwd"></td>
        </tr>
        <tr style="text-align: center">
            <td colspan="2"><input type="submit" value="登录"></td>
        </tr>
    </table>

    <hr>

    <table>
        <tr>
            <td>用户名:</td>
            <td>{{ mc.inp(name="uname", value=nick) }}</td>
        </tr>
        <tr>
            <td>密码:</td>
            <td>{{ mc.inp("password", "pwd") }}</td>
        </tr>
        <tr style="text-align: center">
            <td colspan="2">{{ mc.inp("submit", value="登录") }}</td>
        </tr>
    </table>

    <hr>

    <table>
        <tr>
            <td>用户名:</td>
            <td>{{ inp(name="uname", value=nick) }}</td>
        </tr>
        <tr>
            <td>密码:</td>
            <td>{{ inp("password", "pwd") }}</td>
        </tr>
        <tr style="text-align: center">
            <td colspan="2">{{ inp("submit", value="登录") }}</td>
        </tr>
    </table>
</body>

</html>

common.html

{% macro inp(type="text", name="", value="") %}
    <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}

如果后台传递的信息过多,我们使用宏时一个一个赋值太麻烦了;又或者有许多后台数据只需要在宏定义的时用到,使用宏时并不做改变,那么该怎样将当前页面接收到的后台数据传递给宏文件呢?

如果想要在导入宏的时候,就把当前模版的一些参数传给宏所在的模版,那么就应该在导入的时候添加 with context,这样在模板中就可以使用后台传递的数据了。

{% import "macros/common.html" as usr with context %}

完整示例:

from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    nick = "小龙女"
    return render_template("index.html", nick=nick)


if __name__ == '__main__':
    app.run(debug=True)

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>宏的引入</title>
</head>

<!--
    Python 中引入文件的两种方式:
        import file
        from file import func_name
-->

{#{% import 'macros/common.html' as mc %}#}
{% import 'macros/common.html' as mc with context %}
{#{% from 'macros/common.html' import inp %}#}
{% from 'macros/common.html' import inp with context %}

<body>
    <h1>宏的引入</h1>
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="uname" value="{{ nick }}"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="pwd"></td>
        </tr>
        <tr style="text-align: center">
            <td colspan="2"><input type="submit" value="登录"></td>
        </tr>
    </table>

    <hr>

    <table>
        <tr>
            <td>用户名:</td>
            <td>{{ mc.inp(name="uname") }}</td>
        </tr>
        <tr>
            <td>密码:</td>
            <td>{{ mc.inp("password", "pwd") }}</td>
        </tr>
        <tr style="text-align: center">
            <td colspan="2">{{ mc.inp("submit", value="登录") }}</td>
        </tr>
    </table>

    <hr>

    <table>
        <tr>
            <td>用户名:</td>
            <td>{{ inp(name="uname") }}</td>
        </tr>
        <tr>
            <td>密码:</td>
            <td>{{ inp("password", "pwd") }}</td>
        </tr>
        <tr style="text-align: center">
            <td colspan="2">{{ inp("submit", value="登录") }}</td>
        </tr>
    </table>
</body>

</html>

common.html

{% macro inp(type="text", name="", value="") %}
    {% if type == "text" %}
        <input type="{{ type }}" name="{{ name }}" value="{{ nick }}">
    {% else %}
        <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
    {% endif %}
{% endmacro %}

13 导入模板 include

看下面小米官网的两个页面导航栏和底部的帮助中心几乎是一样的:



我们在写一个网站的多个页面时,为了风格一致,导航栏以及页面底部的帮助中心及版权声明一般是不会发生变化的,那每个页面都写一遍就显得重复性太高了。我们是否可以将他们抽取出来呢?

上一章我们刚学了宏,很容易想到将它们抽取为一个宏。这样做是可以实现需求的。但是定义宏是为了在使用时传递参数,从而展示不同的页面标签,而帮助中心这些内容完全没有变化,html 代码一模一样,完全不需要传递参数,使用宏就显得没有什么必要了。

这时使用 include 是最好的选择:

  1. 这个标签相当于是直接将指定的模版中的代码复制粘贴到当前位置。
  2. include 标签,如果想要使用父模版中的变量,直接用就可以了,不需要使用 with context
  3. include 的路径,也是跟 import 一样,直接从 templates 根目录下去找,不要以相对路径去找。
from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    uname = "小龙女"
    return render_template("index.html", uname=uname)


if __name__ == '__main__':
    app.run(debug=True)

header.html

<header>头部信息</header>

footer.html

<footer>底部信息</footer>
{{ uname }}

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>include的使用</title>
</head>

<body>
    <h1>include的使用</h1>
    {% include 'common/header.html' %}
    <main>主体内容</main>
    {% include 'common/footer.html' %}
</body>

</html>

14 set 与 with 标签

14.1 set 的使用

在模版中,可以使用 set 语句来定义变量。

<!-- set语句来定义变量,之后,那么在后面的代码中,都可以使用这个变量 -->
{% set uname='sxt' %}
<p>用户名:{{ uname }}</p>

一旦定义了这个变量,那么在后面的代码中,都可以使用这个变量,就类似于 Python 的变量定义是一样的。

14.2 with 语句

with 语句定义的变量,只能在 with 语句块中使用,超过了这个代码块,就不能再使用了。

<!-- with语句来定义变量,只有在指定区域 才能使用这个变量 -->
{% with classroom='python202' %}
	<p>班级:{{ classroom }}</p>
{% endwith %}
注意
关于定义的变量,`with` 语句也不一定要跟一个变量,可以定义一个空的 `with` 语句,需要在指定的区域才能使用的情况,可以 `set` 与 `with` 组合使用。
{% with %}
	{% set  pname='李思思' %}
	<p>娱乐县县长:{{ pname }}</p>
{% endwith %}

15 静态文件

15.1 静态文件

静态文件:css 文件 js 文件 图片文件等文件。

加载静态文件使用的是 url_for 函数。然后第一个参数需要为 static,代表引入静态资源,默认静态资源的查找路径是 static;第二个参数需要为一个关键字参数 filename='路径'

语法

{{ url_for("static", filename='xxx') }}

例如

注意
路径查找,要以当前项目的 `static` 目录作为根目录。
from flask import Flask, render_template

app = Flask(__name__)
# app = Flask(__name__, static_folder="static1")


@app.route("/")
def index():
    return render_template("index.html")


if __name__ == '__main__':
    app.run(debug=True)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>静态文件引入</title>
    <!-- <script src="/static/js/index.js" defer></script> -->
    <!-- <link type="text/css" rel="stylesheet" href="/static/css/index.css"> -->
    <script src="{{ url_for("static", filename="/js/index.js") }}" defer></script>
    <link type="text/css" rel="stylesheet" href="{{ url_for("static", filename="/css/index.css") }}">
</head>

<body>
    <h1>静态文件引入</h1>
    <img src="/static/images/img1.jpg">
    <img src="{{ url_for("static", filename="/images/img1.jpg") }}">
</body>

</html>

想要修改静态资源的目录,可以在 app 中进行指定:

app = Flask(__name__, static_folder="static1")

16 模板继承

16.1 为什么需要模版继承

模版继承可以把一些公用的代码单独抽取出来放到一个父模板中以后子模板直接继承就可以使用了。

这样可以重复的利用代码,并且以后修改起来也比较方便。

前面我们已经学过 include 引入模板,和模板继承的差别如下图所示(左为引入模板):

16.2 模版继承语法

使用 extends 语句,来指明继承的父模板。父模板的路径,也是相对于 templates 文件夹下的绝对路径。

{% extends "base.html" %}

16.3 block 语法

一般在父模版中,定义一些公共的代码。子模板可能要根据具体的需求实现不同的代码。

这时候父模版就应该有能力提供一个接口,让子模板来实现。从而实现具体业务需求的功能。

16.4 父模板

{% block block的名字 %}
{% endblock %}

16.5 子模板

{% block block的名字 %}
	子模板中的代码
{% endblock %}

16.4 调用父模版代码 block 中的代码

默认情况下,子模板如果实现了父模版定义的 block。那么子模板 block 中的代码就会覆盖掉父模板中的代码。

如果想要在子模板中仍然保持父模板中的代码,那么可以使用 {{ super() }} 来实现。

父模板

{% block block_body %}
	<p style="background-color: blue">我是父模版block_body处的内容</p>
{% endblock %}

子模板

{% block block_body %}
	{{ super() }}
	<p style="background-color: green">我是子模版block_body处的内容</p>
{% endblock %}

16.5 调用另外一个 block 中的代码

如果想要在另外一个模版中使用其他模版中的代码。那么可以通过 {{ self.其他block名字() }} 就可以了

{% block title %}
	sxt首页
{% endblock %}

{% block block_body %}
	{{ self.title() }}
	<p style="background-color: green">我是子模版block_body处的内容</p>
{% endblock %}

16.6 示例

from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    return render_template("index.html")


if __name__ == '__main__':
    app.run(debug=True)

创建父模板 base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父模板</title>
</head>
<body>
    <h1>头部信息</h1>
    <hr>
    {% block content %}
        <p>这是父模板内容</p>
    {% endblock %}
    <hr>
    <h1>底部信息</h1>
    {% block footer %}
    {% endblock %}
</body>
</html>

后台直接返回 base.html

@app.route("/")
def index():
    return render_template("base.html")

创建子模板 index.html 并继承父模版,后台返回子模板:

@app.route("/")
def index():
    return render_template("index.html")

index.html

{% extends "base.html" %}

子模板实现父模版中的 block,会将父模版中的内容覆盖掉:

{% block content %}
    <p>这是子模板内容</p>
{% endblock %}

保留父模版 block 中的内容:

{% extends "base.html" %}

{% block content %}
    {{ super() }}
    <p>这是子模板内容</p>
{% endblock %}

实现父模版中的 footer block,不论在子模板中 footer 放在什么地方,页面展示的位置还是按父模版中的排列来:

{% extends "base.html" %}

{% block footer %}
    <p>这是底部内容</p>
{% endblock %}

{% block content %}
    {{ super() }}
    <p>这是子模板内容</p>
{% endblock %}

content 块中引用 footer 块:

{% block content %}
    {{ super() }}
    <p>这是子模板内容</p>
    {{ self.footer() }}
{% endblock %}

注意
1. 子模板中的代码,第一行,应该是 `extends`。
> > 2. 子模板中,如果要实现自己的代码,应该放到 block 中。如果放到其他地方,那么就不会被渲染。

17 继承模板实战

实现如下页面,需要使用:

  • 模板继承
  • 引入静态资源
from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/login/")
def login():
    return render_template("login.html")


@app.route("/register/")
def register():
    return render_template("register.html")


if __name__ == '__main__':
    app.run(debug=True)

base.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>{% block title %}首页{% endblock %}</title>


    <link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='css/style.css') }}"/>
    <link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='css/body.css') }}"/>
</head>
<body>
    <div class="container">
        <section id="content">
            {% block content %}

            {% endblock %}
        </section><!-- content -->
    </div>
    <!-- container -->
    <br><br>

</body>
</html>

index.html

{% extends "base.html" %}

{% block title %}首页{% endblock %}

{% block content %}
    <p>首页</p>
{% endblock %}

login.html

{% extends "base.html" %}

{% block title %}登录{% endblock %}

{% block content %}
    <form action="">
        <h1>会员登录</h1>
        <div>
            <input type="text" placeholder="邮箱" required="" id="username"/>
        </div>
        <div>
            <input type="password" placeholder="密码" required="" id="password"/>
        </div>
        <div class="">
            <span class="help-block u-errormessage" id="js-server-helpinfo">&nbsp;</span></div>
        <div>
            <!-- <input type="submit" value="Log in" /> -->
            <input type="submit" value="登录" class="btn btn-primary" id="js-btn-login"/>
            <a href="#">忘记密码?</a>
            <!-- <a href="#">Register</a> -->
        </div>
    </form><!-- form -->
{% endblock %}

register.html

{% extends "base.html" %}

{% block title %}会员注册{% endblock %}

{% block content %}
    <form action="">
        <h1>会员注册</h1>
        <div>
            <input type="text" placeholder="邮箱" required="" id="username"/>
        </div>
        <div>
            <input type="password" placeholder="密码" required="" id="password"/>
        </div>
        <div>
            <input type="password" placeholder="确认密码" required="" id="password"/>
        </div>
        <div class="">
            <span class="help-block u-errormessage" id="js-server-helpinfo">&nbsp;</span></div>
        <div>
            <!-- <input type="submit" value="Log in" /> -->
            <input type="submit" value="注册" class="btn btn-primary" id="js-btn-login"/>
            <a href="#">去登录</a>
            <!-- <a href="#">Register</a> -->
        </div>
    </form><!-- form -->
{% endblock %}
posted @ 2026-04-11 02:04  挖掘鱼  阅读(2)  评论(0)    收藏  举报