Django4.2 学习 - 模板 Template(三)

Django 框架中,模板是可以帮助开发者快速生产呈现给用户页面的工具。

模板的设计方式实现了我们 MTV(M:Model,V:View,T:Template) 中 VT 的解耦(解耦:是指降低软件模块之间的相互依赖程度),VT 有着 N:M 的关系,一个 V 可以调用任意 T,一个 T 可以工任意 V 使用。与 Java 的 MVC 很像,M 为 Model, V 为 View 界面, C 为 Controller 控制器。

模板的处理分为 2 个过程:

  • 加载 HTML
  • 渲染数据(render ( ))

模板主要有 2 个部分

  • HTML 静态代码
  • 模板语言,动态插入的代码段(“挖坑”,“填坑”)

模板中的动态代码除了做基本的静态填充,还可以实现一些基本的运算,转换和逻辑。

静态页面:页面数据是本地固定的。
动态页面:页面数据来源于后台服务器。

1.Django 模板语法

(1)变量

视图传递给模板的数据,遵守标识符规定

语法:{{var}}

如果变量不存在,则插入空字符串

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<p>name:{{ name }}</p>
<p>message:{{ message }}</p>

<p>列表:{{ items }}</p>
<p>列表第一项:{{ items.0 }}</p>
<p>列表第二项:{{ items.1 }}</p>
<p>列表第三项:{{ items.2 }}</p>

<p>布尔值:{{ is_authenticated }}</p>

<p>字典:{{ user }}</p>
<p>字典取值1:{{ user.items }}</p>
<p>字典取值2:{{ user.user1 }}</p>
<p>字典取值3:{{ user.user1.email2 }}</p>

views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def index(request):
data = {
'name': 'Django',
'message': 'Hello Django!',
# 最好不要有这种items的数据,否则在使用 for 循环时会报错
# 'items': ['item1', 'item2', 'item3'],
'numbers': [1, 2, 3, 4, 5],
'is_authenticated': True,
'user': {
'username': 'john',
# 'items': ['item1', 'item2', 'item3'],
'numbers': [1, 2, 3, 4, 5],
'is_authenticated': True,
'user1': {
'username': 'john',
'email': 'EMAIL',
'email2': 'john@example.com',
},
}
}

return render(request, 'index.html', data)

(2)方法

方法的调用,不能有参数(没有括号)

{{str}}

{{str.upper}}

{{str.isdigit}}

{{dict.key}}

(3)标签

语法 {% tag %}

作用:

  • 加载外部传入的变量
  • 在输出中创建文本
  • 控制循环或逻辑

1️⃣if 语句

  • 单分支
1
2
3
{% if user2.age >= 18 %}
<p>{{ user2.age }}岁,已成年</p>
{% endif %}
  • 双分支
1
2
3
4
5
{% if user2.age >= 18 %}
<p>{{ user2.age }}岁,已成年</p>
{% else %}
<p>{{ user2.age }}岁,未成年</p>
{% endif %}
  • 多分支
1
2
3
4
5
6
7
8
9
{% if user2.age >= 60 %}
<p>{{ user2.age }}岁,老年</p>
{% elif user2.age <= 60 and user2.age >= 30 %}
<p>{{ user2.age }}岁,中年</p>
{% elif user2.age <= 30 and user2.age >= 18 %}
<p>{{ user2.age }}岁,青年</p>
{% else %}
<p>{{ user2.age }}岁,未成年</p>
{% endif %}

2️⃣运算符

  • 判断 truefalse,使用 is 或 is not
1
2
3
4
5
{% if user2.is_delted is False %}
<p>用户没有被删除,正常显示</p>
<p>姓名:{{ user2.username }}</p>
<p>年龄:{{ user2.age }}</p>
{% endif %}
  • 使用 and or
1
2
3
4
5
6
7
8
9
10
11
{% if user2.username == 'jim' and user2.age == 29 %}
<p>显示姓名叫 jim 且 年龄位 29岁的 jim</p>
<p>姓名:{{ user2.username }}</p>
<p>年龄:{{ user2.age }}</p>
{% endif %}

{% if user2.username == 'jim' or user2.age == 29 %}
<p>显示姓名叫 jim 或者 年龄位 29岁的 jim</p>
<p>姓名:{{ user2.username }}</p>
<p>年龄:{{ user2.age }}</p>
{% endif %}
  • 使用 innot in
1
2
3
4
5
6
7
{% if 'blue' in user2.favorite_color %}
<p>用户喜欢的颜色中包含 blue</p>
{% endif %}

{% if 'yellow' not in user2.favorite_color %}
<p>用户喜欢的颜色中不包含 yellow</p>
{% endif %}

3️⃣for 语句

  • 遍历列表
1
2
3
{% for number in numbers %}
<p>{{ number }}</p>
{% endfor %}
  • 循环中的下标
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{% for number in numbers %}
{{ numbers }}
<p>从 1 开始正向下标(forloop.counter): 下标值:{{ forloop.counter }} </p>
<p>从 1 结束反向下标(forloop.revcounter): 下标值:{{ forloop.revcounter }}</p>
<p>从 0 开始正向下标(forloop.counter0): 下标值:{{ forloop.counter0 }}</p>
<p>从 0 结束反向下标:(forloop.revcounter0)下标值:{{ forloop.revcounter0 }}</p>
{% endfor %}

<h5>循环中的第一个元素</h5>
{% for number in numbers %}
{% if forloop.first %}
<p>列表中的第一个元素:{{ number }}</p>
{% endif %}
{% endfor %}

<h5>循环中的最后一个元素</h5>
{% for number in numbers %}
{% if forloop.last %}
<p>列表中的最后一个元素:{{ number }}</p>
{% endif %}
{% endfor %}
  • 带 empty 的 for 语句
1
2
3
4
5
{% for number in numbers2 %}
<p>{{ number }}</p>
{% empty %}
<p>numbers2列表为空</p>
{% endfor %}
  • 遍历字典
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<h5>遍历字典</h5>
{% for key, value in user3.items %}
<p>{{ key }}:{{ value }}</p>
{% endfor %}

<h5>单独遍历字典的key</h5>
{% for key in user3.keys %}
<p>{{ key }}</p>
{% endfor %}

<h5>单独遍历字典的value</h5>
{% for value in user3.values %}
<p>{{ value }}</p>
{% endfor %}
{# 传输的数据中切记不能有类似于 items 这种关键字的数据,否则会报错 #}
  • 循环嵌套
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<h5>循环嵌套</h5>
{% for list in list1 %}
<table border="1">
<tr>
{% for l in list %}
{# forloop.parentloop 表示当前循环的爸爸循环 #}
{# forloop 表示当前循环 #}
<td>{{ l }}-{{ forloop.parentloop.counter}}-{{ forloop.counter }}</td>
{% endfor %}
</tr>
</table>
{% endfor %}


{% for key, value in dict_1.items %}

{% if key == 'numbers' %}
{% for number in value %}
<p>{{ number }}</p>
{% endfor %}
{% endif %}

{% if key == 'dict_2' %}
{% for key in value.keys %}
<p>{{ key }}</p>
{% endfor %}
{% endif %}

{% endfor %}

4️⃣注释

  • 单行注释
1
2
{# 注释内容 #}
{# pycharm快捷键: Command + / #}
  • 多行注释
1
2
3
4
5
6
7
8
9
{% comment %}
注释内容1
注释内容2
{% endcomment %}


{% comment %}
pycharm快捷键:Command + Option + /
{% endcomment %}

5️⃣过滤器

在 Django 模板中,过滤器(Filters)是一种用于在模板中对变量进行处理的工具。它们允许你在不修改原始数据的情况下,对变量进行格式化、转换或其他操作。过滤器通常用于在模板中显示数据时,对数据进行一些简单的处理,以满足特定的显示需求。
以下是一些常见的 Django 模板过滤器及其用法:

①add
1
2
3
4
{# 用于对数值进行加法运算 #}
<p> user2.age:{{ user2.age }}</p>
<p>加法过滤器+10:user2.age|add:10:{{ user2.age|add:10 }}</p>
<p>加法过滤器-10:user2.age|add:10:{{ user2.age|add:-10 }}</p>
②capfirst
1
2
3
{# 将字符串的第一个字符转换为大写 #}
<p> {{ user2.username }}</p>
<p>将字符串的第一个字符转换为大写: {{ user2.username|capfirst }}</p>
③date
1
2
3
{# 将日期格式化为指定的字符串格式,这里必须是日期对象 #}
<p> {{ user2.birthday }}</p>
<p>将日期格式化为指定的字符串格式:{{ user2.birthday|date:"Y-M-d" }}</p>

views.py

1
2
3
4
# 假设user2.birthday是一个日期字符串
birthday_str = "1990-01-01"
# 将日期字符串转换为日期对象
user2_birthday = datetime.strptime(birthday_str, "%Y-%m-%d").date()
④default
1
2
{# 如果变量为假(例如空字符串、空列表、None等),则使用指定的默认值 #}
<p>{{ user10.username|default:'空' }}</p>
⑤default_if_none
1
2
{# 如果变量为None,则使用指定的默认值 #}
<p>:{{ user2.test_none|default_if_none:"这个值是个默认值" }}</p>
⑥dictsort/dictsortreversed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{# 字典从小到大 #}
{{ user2.list_dict }}
<p>按照 name 升序</p>
{{ user2.list_dict|dictsort:'name' }}
<p>按照 age 升序</p>
{{ user2.list_dict|dictsort:'age' }}

<p>升序</p>
{% for l in user2.list_dict|dictsort:'name' %}
<p>{{ l }}</p>
{% endfor %}


{# 字典从大到小 #}
<p>按照 name 降序</p>
{{ user2.list_dict|dictsortreversed:'name' }}
<p>按照 age 降序</p>
{{ user2.list_dict|dictsortreversed:'age' }}

<p>降序</p>
{% for l in user2.list_dict|dictsort:'name' %}
<p>{{ l }}</p>
{% endfor %}

⑦divisibleby
1
2
3
{# 检查数值是否能被指定的数整除 #}
<p>10 是否能被 2 整除:{{ 10|divisibleby:2 }}</p>
<p>11 是否能被 2 整除:{{ 11|divisibleby:2 }}</p>
⑧escape/safe
1
2
3
4
5
6
7
{# escape 对字符串进行HTML转义,防止XSS攻击, 试了下,好像 django 对所有的 HTML 都默认转义了#}
<p>对字符串进行HTML转义,防止XSS攻击。</p>
<p>原始字符串:{{ xsstest|escape }}</p>
{# safe safe过滤器用于标记一个字符串是安全的,即它不包含任何需要转义的HTML特殊字符 #}

<p>字符串:{{ xsstest|safe }}</p>

views.py

1
2
3
4
5
6
7
8
9
10
11
def index(request):
# 假设user2.birthday是一个日期字符串
birthday_str = "1990-01-01"
# 将日期字符串转换为日期对象
user2_birthday = datetime.strptime(birthday_str, "%Y-%m-%d").date()

data = {
'name': 'Django',
'message': 'Hello Django!',
'xsstest': "<script>alert('ceshi')</script>",
......
⑨filesizeformat

filesizeformat 是 Django 模板语言中的一个内置过滤器,用于将文件大小格式化为易读的格式。这个过滤器会自动将字节数转换为更人性化的单位,如 KB、MB、GB 等,并保留适当的小数位数。

过滤器的工作原理如下:
如果文件大小小于 1024 字节,它会直接显示字节数。
如果文件大小在 1024 字节到 1048576 字节之间(即 1 KB 到 1 MB 之间),它会将字节数除以 1024,并显示为 “KB”。
如果文件大小在 1048576 字节到 1073741824 字节之间(即 1 MB 到 1 GB 之间),它会将字节数除以 1048576,并显示为 “MB”。
如果文件大小大于 1073741824 字节(即大于 1 GB),它会将字节数除以 1073741824,并显示为 “GB”。

1
2
3
4
<p>将文件大小格式化为易读的格式</p>
<p>file_size1|filesizeformat:{{ file_size1|filesizeformat }}</p>
<p>file_size2|filesizeformat:{{ file_size2|filesizeformat }}</p>
<p>file_size3|filesizeformat:{{ file_size3|filesizeformat }}</p>

views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def index(request):
# 假设user2.birthday是一个日期字符串
birthday_str = "1990-01-01"
# 将日期字符串转换为日期对象
user2_birthday = datetime.strptime(birthday_str, "%Y-%m-%d").date()


# 获取文件大小
file_path1 = 'templates/index.html'
file_size1 = os.path.getsize(file_path1)

file_path2 = '/Users/dachuan/Downloads/LocalSend-1.16.1.dmg'
file_size2 = os.path.getsize(file_path2)

file_path3 = '/Users/dachuan/Downloads/pycharm-professional-2024.3.1-aarch64.dmg'
file_size3 = os.path.getsize(file_path3)

data = {
'name': 'Django',
'message': 'Hello Django!',
'xsstest': "<script>alert('ceshi')</script>",
'numbers': ['第一项', '第二项', '第三项', '第四项', '第五项'],
'is_authenticated': True,
'file_size1': file_size1,
'file_size2': file_size2,
'file_size3': file_size3,
.......
⑩first/last
1
2
3
4
{#<p>first:返回列表或字符串的第一个元素。</p>#}
<p>{{ numbers|first}}</p>
{#<p>last:返回列表或字符串的最后一个元素。</p>#}
<p>{{ numbers|last}}</p>
⑪length
1
2
{#<p>length:返回列表、字符串或字典的长度。</p>#}
<p>{{ numbers|length}}</p>
⑫lower/upper
1
2
3
4
5
{#<p>lower:将字符串转换为小写。</p>#}
<p>{{ user222.username2.lower }}</p>

{#<p>upper:将字符串转换为大写。</p>#}
<p>{{ user222.username1.upper }}</p>
⑬slice
1
2
{#<p>slice:对列表或字符串进行切片操作。</p>#}
<p>{{ numbers|slice:"1:4" }}</p>
⑭truncatechars
1
2
{#<p>slice:截断字符串并添加省略号, 这里的 10= 7 个字符 + “...” 一共 10 个字符</p>#}
<p>{{ message|truncatechars:10 }}
⑮truncatewords
1
2
{#<p>slice:截断字符串并添加省略号,按单词截断。 这里的 5 代表 5 个单词 + “...”</p>#}
<p>{{ message|truncatewords:5 }}</p>
⑯urlize
1
2
3
<p>urlize:将URL转换为可点击的链接</p>
<p>原链接:{{ baidu }}</p>
<p>转化为可点击的链接:{{ baidu|urlize }}</p>
⑰yesno
1
2
3
4
<p>urlize:将布尔值转换为“yes”、“no”或“maybe”(如果是 None 值则显示 maybe)</p>
<p>testyes:{{ testyes|yesno:"yes,no,maybe" }}</p>
<p>testno:{{ testno|yesno:"yes1,no1,maybe1" }}</p>
<p>testmaybe:{{ testmaybe|yesno:"yes2,no2,maybe2" }}</p>
⑱join
1
2
<p>将一个列表中的元素连接成一个字符串,join:'连接符'</p>
<p>{{ numbers|join:'-' }}</p>

6️⃣HTML 转义

在 Django 模板中,autoescape 标签用于控制是否自动转义变量中的特殊字符。默认情况下,Django 模板会自动转义变量中的特殊字符,以防止跨站脚本攻击(XSS)。

  • 自动转义的作用
    自动转义会将变量中的特殊字符(如 <、>、& 等)转换为 HTML 实体,这样浏览器就不会将这些字符解释为 HTML 标签或 JavaScript 代码。例如,< 会被转换为 <,> 会被转换为 >,& 会被转换为 &。
  • 关闭自动转义
    有时候,你可能需要在模板中输出原始的 HTML 内容,而不希望 Django 对其进行转义。在这种情况下,你可以使用 autoescape off 标签来关闭自动转义。
1
2
3
{% autoescape off %}
{{ raw_html }}
{% endautoescape %}
  • 打开自动转义

如果你在关闭自动转义的代码块中又需要开启自动转义,可以使用 autoescape on 标签。

1
2
3
4
5
6
{% autoescape off %}
{{ raw_html }}
{% autoescape on %}
{{ escaped_html }}
{% endautoescape %}
{% endautoescape %}
  • 注意事项
    使用 autoescape off 时要格外小心,因为它会直接输出原始的 HTML 内容。如果变量中包含恶意的 HTML 或 JavaScript 代码,可能会导致 XSS 攻击。
    只有在你确定变量的内容是安全的情况下,才应该使用 autoescape off。
    如果你需要在模板中输出用户输入的内容,建议始终保持自动转义开启,以确保安全性。
    通过 autoescape 标签,你可以灵活地控制 Django 模板中的自动转义行为,以满足不同的需求。

  • 🌰

1
2
3
4
5
6
7
8
9
10
{% autoescape off %}
<p>因为这个自动转义关闭了,xsstest1 的弹窗也会出现</p>
{{ xsstest1 }}
<p>xsstest1 的弹窗也会出现</p>
{% autoescape on %}
<h5>xsstest2 的弹窗不会出现,HTML标签(如h5)不受autoescape标签的影响,它们总是会被浏览器解析和渲染。</h5>
{{ xsstest2 }}
<p>xsstest2 的弹窗不会出现</p>
{% endautoescape %}
{% endautoescape %}
  • views.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
from datetime import datetime

from django.shortcuts import render
import os


# Create your views here.

def index(request):
# 假设user2.birthday是一个日期字符串
birthday_str = "1990-01-01"
# 将日期字符串转换为日期对象
user2_birthday = datetime.strptime(birthday_str, "%Y-%m-%d").date()

# 获取文件大小
file_path1 = 'templates/index.html'
file_size1 = os.path.getsize(file_path1)

file_path2 = '/Users/dachuan/Downloads/LocalSend-1.16.1.dmg'
file_size2 = os.path.getsize(file_path2)

file_path3 = '/Users/dachuan/Downloads/pycharm-professional-2024.3.1-aarch64.dmg'
file_size3 = os.path.getsize(file_path3)

data = {
'name': 'Django',
'message': 'Hello Django ! I am Tester, I am 28 years old.',
'baidu':'http://www.baidu.com/',
'testyes': True,
'testno': False,
'testmaybe': None,

'xsstest': "<script>alert('ceshi')</script>",
'xsstest1': "<script>alert('ceshi1')</script>",

'numbers': ['第一项', '第二项', '第三项', '第四项', '第五项'],
'is_authenticated': True,
'file_size1': file_size1,
'file_size2': file_size2,
'file_size3': file_size3,

'user222': {
'username1': 'john',
'username2': 'JIM',
'numbers': [1, 2, 3, 4, 5],
},
'user2': {
'username': 'jim',
'age': 29,
'favorite_color': ['blue', 'red', 'green'],
'birthday': user2_birthday,
'is_delted': False,
'test_none': None,
'list_dict': [
{'name': 'name5', 'age': 18},
{'name': 'name4', 'age': 19},
{'name': 'name3', 'age': 20},
{'name': 'name1', 'age': 22},
{'name': 'name2', 'age': 21},
]
},

'user3': {
'username': 'jj',
'age': 30,
'favorite_color': ['yellow', 'red', 'green'],
'is_delted': False,
'test': {'k1': 'v1', 'k2': 'v2'},
'numbers': [1, 2, 3, 4, 5],
},
'list1': [
['Test1', 'Test2', 'Test3', 'Test4'],
['Java1', 'Java2', 'Java3', 'Java4'],
['Json1', 'Json2', 'Json3', 'Json4'],
],

'dict_1': {
'name': 'Django',
'message': 'Hello Django!',
'numbers': ['第一项', '第二项', '第三项', '第四项', '第五项'],
'dict_2': {
'username': 'john',
'numbers': [1, 2, 3, 4, 5],
},
},

}

return render(request, 'index.html', data, )


def home(request):
return render(request, 'home.html')

def about(request):
return render(request, 'about.html')
  • index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Django模板</title>
</head>
<body>
<h2>Django模板</h2>
<hr>
{#单行注释:快捷键 Command + / #}
{% comment %}


多行注释:
快捷键 Command + Option + /
{% endcomment %}

{# 变量 #}
<h3>变量</h3>
<hr>
<p>name:{{ name }}</p>
<p>message:{{ message }}</p>

<p>列表:{{ items }}</p>
<p>列表第一项:{{ items.0 }}</p>
<p>列表第二项:{{ items.1 }}</p>
<p>列表第三项:{{ items.2 }}</p>

<p>布尔值:{{ is_authenticated }}</p>

<p>字典:{{ user }}</p>
<p>字典取值1:{{ user.items }}</p>
<p>字典取值2:{{ user.user1 }}</p>
<p>字典取值3:{{ user.user1.email2 }}</p>
<hr>
<h3>标签</h3>
<hr>
<h4>if语句</h4>
<hr>
<h5>单分支</h5>
{% if user2.age >= 18 %}
<p>{{ user2.age }}岁,已成年</p>
{% endif %}

<h5>双分支</h5>
{% if user2.age >= 18 %}
<p>{{ user2.age }}岁,已成年</p>
{% else %}
<p>{{ user2.age }}岁,未成年</p>
{% endif %}

<h5>多分支</h5>
{% if user2.age >= 60 %}
<p>{{ user2.age }}岁,老年</p>
{% elif user2.age <= 60 and user2.age >= 30 %}
<p>{{ user2.age }}岁,中年</p>
{% elif user2.age <= 30 and user2.age >= 18 %}
<p>{{ user2.age }}岁,青年</p>
{% else %}
<p>{{ user2.age }}岁,未成年</p>
{% endif %}

<h5>逻辑运算</h5>
<hr>
<h5>判断 `true`或 `false`</h5>
{% if user2.is_delted is not Ture %}
<p>用户没有被删除,正常显示</p>
<p>姓名:{{ user2.username }}</p>
<p>年龄:{{ user2.age }}</p>
{% endif %}

<h5>使用 `and or not`</h5>

{% if user2.username == 'jim' and user2.age == 29 %}
<p>显示姓名叫 jim 且 年龄位 29岁的 jim</p>
<p>姓名:{{ user2.username }}</p>
<p>年龄:{{ user2.age }}</p>
{% endif %}

{% if user2.username == 'jim' or user2.age == 29 %}
<p>显示姓名叫 jim 或者 年龄位 29岁的 jim</p>
<p>姓名:{{ user2.username }}</p>
<p>年龄:{{ user2.age }}</p>
{% endif %}


<h5>使用`in` 和 `not in`</h5>

{% if 'blue' in user2.favorite_color %}
<p>用户喜欢的颜色中包含 blue</p>
{% endif %}

{% if 'yellow' not in user2.favorite_color %}
<p>用户喜欢的颜色中不包含 yellow</p>
{% endif %}

<h4>for语句</h4>
<hr>
<h5>遍历列表</h5>
{% for number in numbers %}
<p>{{ number }}</p>
{% endfor %}

<h5>循环中的下标</h5>
{% for number in numbers %}
{{ numbers }}
<p>从 1 开始正向下标(forloop.counter): 下标值:{{ forloop.counter }} </p>
<p>从 1 结束反向下标(forloop.revcounter): 下标值:{{ forloop.revcounter }}</p>
<p>从 0 开始正向下标(forloop.counter0): 下标值:{{ forloop.counter0 }}</p>
<p>从 0 结束反向下标:(forloop.revcounter0)下标值:{{ forloop.revcounter0 }}</p>
{% endfor %}

<h5>循环中的第一个元素</h5>
{% for number in numbers %}
{% if forloop.first %}
<p>列表中的第一个元素:{{ number }}</p>
{% endif %}
{% endfor %}

<h5>循环中的最后一个元素</h5>
{% for number in numbers %}
{% if forloop.last %}
<p>列表中的最后一个元素:{{ number }}</p>
{% endif %}
{% endfor %}

<h5>带 empty 的 for 语句</h5>

{% for number in numbers2 %}
<p>{{ number }}</p>
{% empty %}
<p>numbers2列表为空</p>
{% endfor %}


<h5>遍历字典</h5>
{% for key, value in user3.items %}
<p>{{ key }}:{{ value }}</p>
{% endfor %}

<h5>单独遍历字典的key</h5>
{% for key in user3.keys %}
<p>{{ key }}</p>
{% endfor %}

<h5>单独遍历字典的value</h5>
{% for value in user3.values %}
<p>{{ value }}</p>
{% endfor %}

<h5>循环嵌套</h5>
{% for list in list1 %}
<table border="1">
<tr>
{% for l in list %}
{# forloop.parentloop 表示当前循环的爸爸循环 #}
{# forloop 表示当前循环 #}
<td>{{ l }}-{{ forloop.parentloop.counter }}-{{ forloop.counter }}</td>
{% endfor %}
</tr>
</table>
{% endfor %}

{% for key, value in dict_1.items %}

{% if key == 'numbers' %}
{% for number in value %}
<p>{{ number }}</p>
{% endfor %}
{% endif %}

{% if key == 'dict_2' %}
{% for key in value.keys %}
<p>{{ key }}</p>
{% endfor %}
{% endif %}

{% endfor %}

<h5>过滤器 Filters</h5>

<p> user2.age:{{ user2.age }}</p>
<p>加法过滤器+10:user2.age|add:10:{{ user2.age|add:10 }}</p>
<p>加法过滤器-10:user2.age|add:10:{{ user2.age|add:-10 }}</p>

<p> {{ user2.username }}</p>
<p>将字符串的第一个字符转换为大写: {{ user2.username|capfirst }}</p>

<p> {{ user2.birthday }}</p>
<p>将日期格式化为指定的字符串格式: {{ user2.birthday|date:"Y-M-d" }}</p>

<p>如果变量为假(例如空字符串、空列表、None等),则使用指定的默认值:{{ user10.username|default:'空' }}</p>

<p>如果变量为None,则使用指定的默认值:{{ user2.test_none|default_if_none:"这个值是个默认值" }}</p>
<p>排序前</p>
{% for key, value in user2.dict_1.items %}
<p>{{ key }}:{{ value }}</p>
{% endfor %}

{{ user2.list_dict }}
<p>按照 name 升序</p>
{{ user2.list_dict|dictsort:'name' }}
<p>按照 age 升序</p>
{{ user2.list_dict|dictsort:'age' }}

<p>升序</p>
{% for l in user2.list_dict|dictsort:'name' %}
<p>{{ l }}</p>
{% endfor %}

{{ user2.list_dict }}
<p>按照 name 降序</p>
{{ user2.list_dict|dictsort:'name' }}
<p>按照 age 降序</p>
{{ user2.list_dict|dictsort:'age' }}

<p>降序</p>
{% for l in user2.list_dict|dictsort:'name' %}
<p>{{ l }}</p>
{% endfor %}

<p>检查数值是否能被指定的书整除</p>
<p>10 是否能被 2 整除:{{ 10|divisibleby:2 }}</p>
<p>11 是否能被 2 整除:{{ 11|divisibleby:2 }}</p>

<p>对字符串进行HTML转义,防止XSS攻击。</p>
原始字符串:{{ xsstest|escape }}
<p>转义后的字符串:{{ xsstest|safe }}</p>

<p>将文件大小格式化为易读的格式</p>
<p>file_size1|filesizeformat:{{ file_size1|filesizeformat }}</p>
<p>file_size2|filesizeformat:{{ file_size2|filesizeformat }}</p>
<p>file_size3|filesizeformat:{{ file_size3|filesizeformat }}</p>

<p>first:返回列表或字符串的第一个元素。</p>
<p>{{ numbers|first }}</p>

<p>last:返回列表或字符串的最后一个元素。</p>
<p>{{ numbers|last }}</p>

<p>length:返回列表、字符串或字典的长度。</p>
<p>{{ numbers|length }}</p>

<p>lower:将字符串转换为小写。</p>
<p>{{ user222.username2.lower }}</p>

<p>upper:将字符串转换为大写。</p>
<p>{{ user222.username1.upper }}</p>

<p>slice:对列表或字符串进行切片操作。</p>
<p>{{ numbers|slice:"1:4" }}</p>


<p>truncatechars:截断字符串并添加省略号。</p>
<p>{{ message|truncatechars:10 }}

<p>truncatewords:截断字符串并添加省略号,按单词截断。</p>
<p>{{ message|truncatewords:5 }}</p>

<p>urlize:将URL转换为可点击的链接</p>
<p>原链接:{{ baidu }}</p>
<p>转化为可点击的链接:{{ baidu|urlize }}</p>

<p>urlize:将布尔值转换为“yes”、“no”或“maybe”(如果是 None 值则显示 maybe)</p>
<p>testyes:{{ testyes|yesno:"yes,no,maybe" }}</p>
<p>testno:{{ testno|yesno:"yes1,no1,maybe1" }}</p>
<p>testmaybe:{{ testmaybe|yesno:"yes2,no2,maybe2" }}</p>

<p>将一个列表中的元素连接成一个字符串,join:'连接符'</p>
<p>{{ numbers|join:'-' }}</p>

{% autoescape off %}
<p>因为这个自动转义关闭了,xsstest1 的弹窗也会出现</p>
{{ xsstest1 }}
<p>xsstest1 的弹窗也会出现</p>
{% autoescape on %}
<h5>xsstest2 的弹窗不会出现,HTML标签(如h5)不受autoescape标签的影响,它们总是会被浏览器解析和渲染。</h5>
{{ xsstest2 }}
<p>xsstest2 的弹窗不会出现</p>
{% endautoescape %}
{% endautoescape %}

</body>
</html>

7️⃣模板继承

在 Django 模板中,block、extends、include 和 block.super 是用于模板继承和复用的重要标签。下面是一个使用这四个标签的学习例子:
假设我们有一个基础模板 base.html,它定义了网站的基本结构,包括头部、导航栏、内容区域和页脚。我们还希望在不同的页面中可以自定义内容区域的内容,同时保留基础模板的其他部分。

  • 基础模板 base.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}Default Title{% endblock %}</title>
</head>
<body>
<header>
<h1>My Website</h1>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about/">About</a></li>
<li><a href="/contact/">Contact</a></li>
</ul>
</nav>
</header>

<main>
{% block content %}{% endblock %}
</main>

<footer>
<p>&copy; 2023 My Website. All rights reserved.</p>
</footer>
</body>
</html>

在这个基础模板中,我们使用了 block 标签来定义两个可替换的区域:title 和 content。这些区域将在子模板中被填充。

  • 子模板 home.html
1
2
3
4
5
6
7
8
{% extends 'base.html' %}

{% block title %}Home Page{% endblock %}

{% block content %}
<h2>Welcome to the Home Page</h2>
<p>This is the content of the home page.</p>
{% endblock %}

在这个子模板中,我们使用 extends 标签来指定继承的基础模板。然后,我们使用 block 标签来填充基础模板中的 title 和 content 区域。

  • 子模板 about.html
1
2
3
4
5
6
7
8
9
{% extends 'base.html' %}

{% block title %}About Us{% endblock %}

{% block content %}
<h2>About Us</h2>
<p>We are a team of passionate developers.</p>
<p>Our mission is to create amazing websites.</p>
{% endblock %}

同样,这个子模板也继承了 base.html,并填充了 title 和 content 区域。

  • 使用 include 标签:
1
2
3
4
5
6
7
8
<!-- sidebar.html -->
<div class="sidebar">
<h3>Sidebar</h3>
<ul>
<li><a href="/news/">News</a></li>
<li><a href="/events/">Events</a></li>
</ul>
</div>

然后在 home.htmlabout.html 中引入这个侧边栏:

1
2
3
4
5
6
7
8
9
10
<!-- home.html -->
{% extends 'base.html' %}

{% block title %}Home Page{% endblock %}

{% block content %}
<h2>Welcome to the Home Page</h2>
<p>This is the content of the home page.</p>
{% include 'sidebar.html' %}
{% endblock %}
1
2
3
4
5
6
7
8
9
10
11
12
<!-- about.html -->
{% extends 'base.html' %}

{% block title %}About Us{% endblock %}

{% block content %}
<h2>About Us</h2>
<p>We are a team of passionate developers.</p>
<p>Our mission is to create amazing websites.</p>
{% include 'sidebar.html' %}
{% endblock %}

  • 使用 block.super 标签:

有时候,我们希望在子模板中保留基础模板中某个 block 区域的内容,并在其基础上添加一些额外的内容。这时可以使用 block.super 标签。
假设我们希望在 about.html 的 content 区域中保留基础模板中的内容,并在其下方添加一些额外的信息.

1
2
3
4
5
6
7
8
9
10
<!-- about.html -->
{% extends 'base.html' %}

{% block title %}About Us{% endblock %}

{% block content %}
{{ block.super }}
<h3>Our Team</h3>
<p>We have a diverse team of developers, designers, and marketers.</p>
{% endblock %}

2. 在 Django 项目中使用 Jinja2 模板引擎

在 Django 中使用 Jinja2 模板可以按照以下步骤进行配置和使用:

(1)安装 Jinja2

1
pip install Jinja2

(2)配置 Django 以支持 Jinja2

在 Django 项目的 settings.py 文件中,需要对 TEMPLATES 配置项进行修改,以支持 Jinja2 模板引擎。具体配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
TEMPLATES = [

#
{
'BACKEND': 'django.template.backends.jinja2.Jinja2',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'environment': 'DjangoPro5.jinja2.jinja2_env.environment', # 指定环境配置函数
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},

# 保留默认模板引擎(复制一份即可)
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]

(3)创建 Jinja2 环境配置

在项目的某个应用目录下(例如 DjangoPro5),创建一个名为 jinja2 的文件夹,并在该文件夹中创建一个名为 jinja2_env.py 的文件。在 jinja2_env.py` 文件中定义 Jinja2 的环境配置:

1
2
3
4
5
6
7
8
9
10
11
from jinja2 import Environment
from django.templatetags.static import static
from django.urls import reverse

def environment(**options):
env = Environment(**options)
env.globals.update({
'static': static, # 用于访问静态文件
'url': reverse, # 用于生成 URL
})
return env

(4)编写 Jinja2 模板

templates 文件夹中创建 Jinja2 模板文件,例如 index.html,并使用 Jinja2 的语法编写模板内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello</title>
</head>
<body>
<h1>Hello</h1>

{% for i in range(10) if i % 2 == 0 %} {# jinja2 模板支持函数如 range(10) #}
{{ i }}
{% endfor %}


</body>
</html>

(5)在视图中使用 Jinja2 模板

在 Django 的视图中,可以像使用默认模板引擎一样使用 Jinja2 模板。例如:

1
2
3
4
5
6
7
8
from django.shortcuts import render

def hello_view(request):
context = {
'name': 'Alice',
'items': ['apple', 'banana', 'cherry'],
}
return render(request, 'hello.html', context)

(6)与 Django 模板的差异

jinja2 模板与 Django 模板对比总结

3. 相关参考

bilibili 教程