Flask中的Jinja2简单用法

在Python中,该漏洞常见于Flask(一个轻量级Web应用框架)模块中,Flask使用Jinja2作为模板引擎

模板

  • {{...}}:将花括号内的内容作为表达式执行并返回对应结果。

    1
    2
    # 会被解析为49
    {{7*7}}
  • {%...%}:用于声明变量或条件/循环语句

    1
    2
    3
    4
    5
    6
    # 使用set声明变量
    {% set name='ikun' %}
    # 条件语句
    {% if var is true %}ikun{%endif%}
    # 循环语句
    {% for i in range(6) %}ikun{%endfor%}
  • ``:注释

详细用法

变量

全局变量

使用set语句

1
{% set name='ikun' %}

之后就可以在页面文件中使用name这个变量了

局部变量

使用with语句来创建一个内部作用域

1
2
3
{% with name='ikun' %}
{{ name }}
{% endwith %}

name变量只能在with标签中使用

控制语句

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

if语句

1
2
3
4
5
6
7
{% if user.is_admin %}
<p>Welcome, administrator!</p>
{% elif user.is_logged_in %}
<p>Welcome back, {{ user.name }}!</p>
{% else %}
<p>Please log in.</p>
{% endif %}

for语句

普通用法

1
2
3
4
5
6
7
<u1>
{% for user in users %}
<li>{{ user.username }}</li>
{% else %}
<li>no users found</li>
{% endfor %}
</u1>

遍历字典

1
2
3
4
{% for key,value in my_dict.items() %}
<dt>{{ key }}</dt>
<dd>{{ value }}</dd>
{% endfor %}

循环变量loop

1
2
3
4
5
6
7
8
9
10
11
12
13
{% for item in items %}
{{ loop.index }} {# 当前迭代次数(从1开始) #}
{{ loop.index0 }} {# 当前迭代次数(从0开始) #}
{{ loop.revindex }} {# 反向迭代次数(从1开始) #}
{{ loop.revindex0 }} {# 反向迭代次数(从0开始) #}
{{ loop.first }} {# 是否是第一次迭代(布尔值) #}
{{ loop.last }} {# 是否是最后一次迭代(布尔值) #}
{{ loop.length }} {# 序列长度 #}
{{ loop.previtem }} {# 上一个迭代项 #}
{{ loop.nextitem }} {# 下一个迭代项 #}
{{ loop.depth }} {# 当前循环的嵌套深度(从1开始) #}
{{ loop.depth0 }} {# 当前循环的嵌套深度(从0开始) #}
{% endfor %}

运算符

  • +号运算符:数字相加,字符串相加,列表相加。但是并不推荐使用+运算符来操作字符串,字符串相加应该使用~运算符。
  • -号运算符:只能对两个数字相减。
  • /号运算符:对两个数进行相除。
  • %号运算符:取余运算。
  • *号运算符:乘号运算符,并且可以对字符进行相乘。
  • **号运算符:次幂运算符,比如2**3=8。
  • in操作符:跟python中的in一样使用,比如{{1 in [1,2,3]}}返回true
  • ~号运算符:拼接多个字符串,比如{{"Hello" ~ "World"}}将返回HelloWorld

宏(非常重要)

宏是 Jinja2 中的一种可重用代码块,类似于其他编程语言中的函数

宏相当于一个搭建好的页面一部分,可以被引入,可以往宏传递参数。可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个变量,在使用宏时传递参数,从而将宏渲染成为页面的一部分

定义和调用宏

1
2
3
4
5
6
7
8
{# 定义宏 #}
{% macro 宏名称(参数1, 参数2=默认值) %}
{# 宏内容 #}
{{ 参数1 }} - {{ 参数2 }}
{% endmacro %}

{# 调用宏 #}
{{ 宏名称(值1, 参数2=值2) }}

如定义和调用一个input标签宏

1
2
3
4
5
6
7
8
9
10
11
12
{% macro inpput(name,value='',type='text',class='') %}
<input type="{{ type }}"
name="{{ name }}"
value="{{ value }}"
class="form-control {{ class }}">
{% endmacro %}

<form>
{{ input('username')}}
{{ input('password',type='password')}}
{{ input('email',type='email',class='large')}}
</form>

页面文件中导入宏(import)

在开发中,会将一些常用的宏单独放在一个文件中,需要时再从这个文件中导入

py中import解析

1.直接导入整个模块

1
import math

2.导入模板并设置别名

1
import numpy as np

3.导入特定函数/类

1
from math import sqrt,pi

4.导入并设置别名

1
from datetime import datetime as dt
1
2
3
4
5
6
7
8
{% import 'forms.html' as forms %}  //导入宏文件
<dl>
<dt>Username</dt>
<dd>{{ forms.input('username') }}</dd> //使用宏
<dt>Password</dt>
<dd>{{ forms.input('password', type='password') }}</dd>
</dl>
<p>{{ forms.textarea('comment') }}</p>

导入模板并不会把当前上下文中的变量添加到被导入的模板中,我们可以在导入的时候使用with context 把上下文传进去:

1
{% from 'forms.html' import my_macro with context %}

宏文件中引用其他宏(include)

include语句可以把一个模板引入到另外一个模板中,类似于把一个模板的代码copy到另外一个模板的指定位置

1
2
3
4
5
6
7
8
{# greeting.html #}
<p>hello,{{ username }}!</p>

{# profile.html #}
{% set username = "ikun" %}
<div class="profile">
{% include 'greeting.html' %} {# 可以访问当前上下文的变量 #}
</div>

模板文件的继承

模板允许构建一个基础"骨架"模板,然后让子模板继承并覆盖特定部分

基础模板(base.html):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
{% block head %}
<title>{% block title %}默认标题{% endblock %}</title>
{% endblock %}
</head>
<body>
{% block content %}
<!-- 默认内容 -->
<p>这是基础模板的内容</p>
{% endblock %}
</body>
</html>

子模板(child.html):

1
2
3
4
5
6
7
8
{% extends "base.html" %}

{% block title %}子页面标题{% endblock %}

{% block content %}
{{ super() }} {# 保留父模板内容 #}
<p>这是子模板添加的内容</p>
{% endblock %}
  1. {% extends %} 指令
  • 必须是模板的第一个标签
  • 指定要继承的父模板路径
  • 子模板中未定义的块将使用父模板的默认内容
  1. {% block %}` 标签 - 定义可被覆盖的内容区块 - 基本语法:`{% block block_name %}内容{% endblock %}
  • 支持嵌套 block
  1. {{ super() }} 函数
  • 在子模板中调用父模板的 block 内容
  • 可以控制父模板内容的插入位置

模板文件中对block内容的调用,可以使用 self.blockName 的方式

1
2
<title>{% block title %}{% endblock %}</title>
<h1>{{ self.title() }}</h1> {# 调用title block的内容 #}

过滤器

过滤器是通过(|)符号进行使用的,其相当于是一个函数,把当前的变量传入到过滤器中,然后过滤器根据自己的功能,再返回相应的值,之后再将结果渲染到页面中

分类 过滤器 功能描述 使用示例 输出结果
字符串处理 lower 转换为小写 {{ "HELLO" | lower }} "hello"
upper 转换为大写 {{ "hello" | upper }} "HELLO"
capitalize 首字母大写 {{ "hello" | capitalize }} "Hello"
trim 去除首尾空格 {{ " hello " | trim }} "hello"
replace(old, new) 字符串替换 {{ "Hi Tom" | replace("Tom", "Alice") }} "Hi Alice"
truncate(length=255) 截断字符串 {{ "Long text" | truncate(4) }} "Long..."
列表/字典 first 获取第一个元素 {{ [1,2,3] | first }} 1
last 获取最后一个元素 {{ [1,2,3] | last }} 3
length 获取元素数量 {{ "hello" | length }} 5
join(delimiter) 连接列表元素 {{ ["A","B"] | join("--") }} "A--B"
sort 排序列表 {{ [3,1,2] | sort }} [1,2,3]
map(attribute) 提取对象属性 {{ users | map(attribute='name') }} 用户名列表
数值处理 round(precision) 四舍五入 {{ 3.14159 | round(2) }} 3.14
abs 绝对值 {{ -5 | abs }} 5
int / float 类型转换 {{ "3.14" | float }} 3.14
日期时间 datetimeformat(format) 格式化日期 {{ now | datetimeformat("%Y/%m/%d") }} "2023/08/15"
date 提取日期部分 {{ now | date }} "2023-08-15"
逻辑处理 default(value) 设置默认值 {{ name | default("匿名") }} 变量为空时输出"匿名"
selectattr(condition) 条件筛选列表 {{ users | selectattr("is_active") }} 活跃用户列表
rejectattr(condition) 反向筛选列表 {{ users | rejectattr("is_banned") }} 非封禁用户列表
HTML安全 escape / e HTML转义(防XSS) {{ "
共发表 44 篇 Blog · 总计 109.6k 字