6. Django 深入模板

image-20240415213351376

6. 深入模板

6.1 Django模板引擎

Django内置的模板引擎包含模板上下文(亦可称为模板变量), 标签和过滤器, 各个功能说明如下:
 模板上下文是以变量的形式写入模板文件里面, 变量值由视图函数或视图类传递所得.
 标签是对模板上下文进行控制输出, 比如模板上下文的判断和循环控制等.
 模板继承隶属于标签, 它是将每个模板文件重复的代码抽取出来并写在一个共用的模板文件中,其他模板文件通过继承共用模板文件来实现完整的网页输出.
 过滤器是对模板上下文进行操作处理, 比如模板上下文的内容截取, 替换或格式转换等.

6.1.1 模板上下文

模板上下文是模板中基本的组成单位, 上下文的数据由视图函数或视图类传递.
它以{{ variable }}表示, variable是上下文的名称, 它支持Python所有的数据类型, 如字典, 列表, 元组, 字符串, 整型或实例化对象等.
上下文的数据格式不同, 在模板里的使用方式也有所差异, 如下所示:
# 1. 假如: variable1 = '字符串或整型'
<div>{{ variable1 }}</div>
# 输出'<div>字符串或整型</div>'# 2. 假如: variable2 = {'name': '字典或实例化对象'}
<div>{{ variable2.name }}</div>
# 输出'<div>字典或实例化对象</div>'# 3. 假如: variable3 = ['元组或列表']
<div>{{ variable3.0 }}</div>
# 输出“<div>元组或列表</div>
从上述代码发现, 如果上下文的数据带有属性, 就可以在上下文的末端使用'.'来获取某个属性的值.
比如上下文为字典或实例化对象, 在上下文末端使用'.'并写入属性名称即可在网页上显示该属性的值;
若上下文为元组或列表, 则在上下文末端使用'.'并设置索引下标来获取元组或列表的某个元素值.
如果视图没有为模板上下文传递数据或者模板上下文的某个属性, 索引下标不存在, Django就会将其设为空值.
例如获取variable2的属性age, 由于上述的variable2并不存在属性age, 因此网页上将会显示'<div></div>'';.
在PyCharm的Debug调试模式里分析Django模板引擎的运行过程.
打开函数render所在的源码文件, 变量content是模板文件的解析结果, 它是由函数render_to_string完成解析过程的, 如图6-1所示.

2024-04-15_021721

6-1 函数render的源码信息
想要分析Django模板引擎的解析过程, 还需要从函数render_to_string深入分析,
通过PyCharm打开函数render_to_string的源码信息, 发现它调用了函数get_template或select_template,
我们沿着函数调用的方向去探究整个解析过程, 梳理函数之间的调用关系, 最终得出模板解析过程, 如图6-2所示.
整个解析过程调用了多个函数和类方法, 每个函数和类方法在源码里都有功能注释, 这里不再详细讲述, 读者可自行在源码里查阅.

image-20240415023551065

6-2 Django模板引擎的解析过程

6.1.2 自定义标签

标签是对模板上下文进行控制输出, 它是以{% tag %}表示的, 其中tag是标签的名称, 
Django内置了许多模板标签, 比如{% if %} (判断标签), {% for %} (循环标签)  {% url %} (路由标签).
内置的模板标签可以在Django源码(\django\template\defaulttags.py)里找到定义过程,
每个内置标签都有功能注释和使用方法, 本书只列举常用的内置标签, 如表6-1所示.

image-20240415115510893

6-1 常用的内置标签
标签描述
{% for %}用于遍历列表, 元组, 字典或任何可迭代对象, 并输出上下文的内容.
{% if %}对上下文中的变量进行条件判断, 根据条件执行不同的模板代码块.
{% csrf_token %}生成一个隐藏的表单字段, 包含CSRF令牌, 用于防止跨站请求伪造攻击.
{% url %}根据Django的URL配置, 生成一个URL, 它通常用于在模板中链接到其他视图.
{% with %}临时重命名一个或多个变量, 使得在{% with %}和{% endwith %}之间的模板代码可以使用新的变量名.
{% load %}加载一个或多个自定义模板标签库, 使得这些库中的标签可以在当前模板中使用.
{% static %}引用静态文件, 例如CSS, JavaScript或图片文件. 它会自动将文件路径转换为正确的URL.
{% extends %}表示当前模板继承自另一个模板(称为父模板).
这允许你创建一个包含通用页面元素(如头部和底部)的基础模板, 并在其他模板中继承这些元素.
{% block %}在子模板中定义可重写的代码块. 在父模板中, 可以使用{% block %}标签来定义默认内容,
而在子模板中, 可以使用相同的 {% block %}标签来重写这些内容.
在上述常用标签中, 每个标签的使用方法都是各不相同的,
通过简单的例子来进一步了解标签的使用方法, 代码如下:
# for标签
{% for item in my_list %}
{{ item }}
{% endfor %}# for标签, 支持嵌套, my_list可为列表, 元组或某个对象
# item可自定义命名, 代表当前循环的数据对象
# {% endfor %} 是循环区域终止符, 代表这区域的代码由标签for输出
# if标签
{% if name == "Lily" %}{{ name }}
{% elif name == "Lucy" %}{{ name }}
{% else %}{{ name }}
{% endif %}
# if标签, 支持嵌套
# 判断条件符与上下文之间使用空格隔开, 否则程序会抛出异常
# {% endif %}与{% endfor %}的作用是相同的(判断终止符, 代码这块区域的代码由标签if输出)
# url标签
# 生成不带变量的URL地址
<a href="{% url 'index' %}">首页</a># 生成带变量的URL地址
<a href="{% url 'page' 1 %}">1</a>
# with标签
{% with total=number %}{{ total }}
{% endwith %}
# with标签, 与Python的with语法的功能相同 
# with标签的功能是创建一个临时的命名空间, 用于在模板的特定部分中存储变量.
# total=number无须空格隔开, 否则抛出异常
# load标签, 导入静态文件标签库staticfiles
# staticfiles来自settings.py的INSTALLED_APPS
{% load staticfiles %}
# static标签, 来自静态文件标签库staticfiles
{% static "css/index.css" %}
在for标签中, 模板还提供了一些特殊的变量来获取for标签的循环信息, 变量说明如表6-2所示.
6-2 for标签模板变量说明
变量描述
forloop.counter获取当前循环的索引, 从1开始计算. 在循环的每一次迭代中, 这个值会递增.
forloop.counter0获取当前循环的索引, 从0开始计算. 在循环的每一次迭代中, 这个值会递增.
forloop.revcounter索引从当前循环的最大数开始递减, 直到递减到1位置. 随着循环的进行, 这个值会递减.
forloop.revcounter0索引从当前循环的最大数开始递减, 直到递减到0位置. 随着循环的进行, 这个值会递减.
forloop.first当遍历的元素为第一项时为真(True). 在循环的第一次迭代中, 这个值将为真.
forloop.last当遍历的元素为最后一项时为真(True). 在循环的最后一次迭代中, 这个值将为真.
forloop.parentloop在嵌套的for循环中, 获取上层for循环的forloop对象.
这使得你可以访问上层循环的相关信息, 如索引或是否为首项/末项等.
上述变量来自于forloop对象, 该对象是在模板引擎解析for标签时生成的.
通过简单的例子来进一步了解forloop的使用, 例子如下:
{% for name in name_list %}{% if forloop.counter == 1 %}<span>这是第一次循环</span>{% elif forloop.last %}<span>这是最后一次循环</span>{% else %}<span>本次循环次数为: {{forloop.counter }}</span>{% endif %}
{% endfor %}
除了使用内置的模板标签之外, 我们还可以自定义模板标签.
以MyDjango为例, 在项目的根目录下创建新的文件夹, 文件夹名称可自行命名, 本示例命名为mydefined;
然后在该文件夹下创建初始化文件__init__.py和templatetags文件夹, 其中templatetags文件夹的命名是固定不变的; 
最后在templatetags文件夹里创建初始化文件__init__.py和自定义标签文件mytags.py, 项目的目录结构如图6-3所示.

2024-04-15_031429

6-3 目录结构
由于在项目的根目录下创建了mydefined文件夹, 因此在配置文件settings.py的属性INSTALLED_APPS里添加mydefined,
否则Django在运行时无法加载mydefined文件夹的内容, 配置信息如下:
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages',	'django.contrib.staticfiles','index',  # Pycharm自动添加的话显示 'index.apps.IndexConfig',# 添加自定义模板标签的文件夹'mydefined'
]

2024-04-15_031711

下一步在项目的mytags.py文件里自定义标签, 我们将定义一个名为reversal的标签,
它是将标签里的数据进行反转处理, 定义过程如下:
from django import template# 创建模板对象
register = template.Library()# 定义模板节点类型
class ReversalNode(template.Node):# 初始化def __init__(self, value):self.value = str(value)# 数据反转处理def render(self, context):return self.value[::-1]# 声明并定义标签
@register.tag(name='reversal')
# parse是解析器对象, token是被解析的对象
def do_reversal(parse, token):try:# tag_name 代表标签签名, 即reversal# value 是由标签传递的数据tag_name, value = token.split_contents()except Exception:raise template.TemplateSyntaxError('syntax')# 调用自定义的模板节点类型return ReversalNode(value)

image-20240415040403248

在mytags.py文件里分别定义了类ReversalNode和函数do_reversal, 两者实现功能说明如下:
 函数do_reversal经过装饰器register.tag(name='reversal')处理, 这是让函数执行模板标签注册,标签名称由装饰器参数name进行命名, 如果没有设置参数name, 就以函数名作为标签名称.函数名没有具体要求, 一般以'do_标签名称''标签名称'作为命名规范. 函数参数parse是解析器对象, 当Django运行时, 它将所有标签和过滤器进行加载并生成到parse对象,在解析模板文件里面的标签时, Django就会从parse对象查找对应的标签信息. 函数参数token是模板文件使用标签时所传递的数据对象, 主要包括标签名和数据内容. 函数do_reversal对参数token使用split_contents()方法(Django的内置方法)进行取值处理,从中获取数据value, 并将value传递给自定义模板节点类ReversalNode.
 类ReversalNode是将value执行字符串反转处理, 并生成模板节点对象, 用于模板引擎解析HTML语言.(render方法会在模板渲染过程中被触发.
具体来说, 当Django模板引擎遇到你的自定义标签{% reversal value %}, 它会执行以下步骤:
* 1. 解析标签: Django模板引擎首先会调用自定义的do_reversal函数.这个函数会解析标签的内容, 并返回一个ReversalNode实例.* 2. 渲染节点: 在模板渲染的过程中, Django模板引擎会遍历模板中的所有节点, 并调用它们的render方法.对于ReversalNode实例, 当它的render方法被调用时, 它会返回反转后的字符串.)
为了验证自定义标签reversal的功能, 我们在index的url.py, views.py和模板文件index.html里编写以下代码:
# index的url.py
from django.urls import path
from .views import *
urlpatterns = [# 定义路由path('', index, name='index'),
]

image-20240415040427355

# index的views.py
from django.shortcuts import render
def index(request):return render(request, 'index.html', locals())

image-20240415040455731

<!-- templates 的 index.html -->
{#导入自定义标签文件mytags#}
{% load mytags %}<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>{% reversal 'Django' %}
</body>
</html>

image-20240415040522381

在模板文件index.html中使用自定义标签时, 必须使用{% load mytags %}将自定义标签文件导入,
告知模板引擎从哪里查找自定义标签, 否则无法识别自定义标签, 并提示TemplateSyntaxError异常.运行MyDjango项目, 在浏览器上访问l 127.0.0.1:8000, 网页上会将'Django'反转显示, 如图6-4所示.

image-20240415040130042

6-4 自定义标签reversal
综上所述, 我们发现自定义标签reversal的定义方式与内置标签的定义方式是相同的, 两者最大的区别在于:
 自定义标签需要在项目里搭建目录环境.
 在使用时需要在模板文件里导入自定义标签文件.

6.1.3 模板继承

模板继承是通过模板标签来实现的, 其作用是将多个模板文件的共同代码集中在一个新的模板文件中,
然后各个模板可以直接调用新的模板文件, 从而生成HTML网页, 这样可以减少模板之间重复的代码, 范例如下:
<!-- templates 的 index.html -->
<!DOCTYPE html>
<html>
<html lang="en">
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body><a href="{% url 'index:index' %}">首页</a><h1>Hello Django</h1>
</body>
</html>
上述代码是一个完整的模板文件, 一个完整的模板通常有<head><body>两部分,
而每个模板的<head><body>的内容都会有所不同, 因此除了这两部分的内容之外, 可以将其他内容写在共用模板文件里.
以MyDjango为例, 在templates文件夹里创建base.html文件, 该文件作为共用模板, 代码如下:
<!-- templates 的 base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
{% block title %}<title>首页</title>
{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>

在base.html的代码中看到, <title>写在模板标签{% block title %}{% endblock %}里面,
<body>里的内容改为{% block body %}{% endblock %}.
block标签是为其他模板文件调用时提供内容重写的接口, body是对这个接口进行命名.
在一个模板中可以添加多个block标签, 只要每个block标签的命名不相同即可.
接着在模板index.html中调用共用模板base.html, 代码如下:
<!-- templates 的 index.html -->
{% extends "base.html" %}{% block body %}<a href="{% url 'index:index' %}">首页</a>
<h1>Hello Django</h1>{% endblock %}

image-20240415121240807

模板index.html调用共用模板base.html的实质是由模板继承实现的, 调用步骤如下:
 在模板index.html中使用{% extends "base.html" %}来继承模板base.html的所有代码.
 通过使用标签{% block title %}{% block body %}来重写模板base.html的网页内容.
 如果没有使用标签block重写共用模板的内容, 网页内容将就由共用模板提供.比如模板index.html没有使用标签{% block title %}重新定义<title>, 那么网页标题内容应由模板base.html设置的<title>提供.
 标签block必须使用{% endblock %}结束block标签.从模板index.html看到, 模板继承与Python的类继承原理是一致的, 通过继承方式使其具有父类的功能和属性,同时也可以通过重写来实现复杂多变的开发需求.
为了验证模板继承是否正确, 运行MyDjango并访问127.0.0.1:8000,
查看网页标题(标题由模板base.html的<title>提供)和网页信息(重写模板base.html的{% block body %}), 如图6-5所示.

2024-04-15_121356

6-5 运行结果

6.1.4 自定义过滤器

过滤器主要是对上下文的内容进行操作处理, 如替换, 反序和转义等.
通过过滤器处理上下文可以将其数据格式或内容转化为我们想要的显示效果, 而且相应减少视图的代码量.过滤器的使用方法如下:
{{ variable | filter }}若上下文设有过滤器, 则模板引擎在解析上下文时, 
首先由过滤器filter处理上下文variable, 然后将处理后的结果进行解析并显示在网页上. 
variable代表模板上下文, 管道符号'|'代表当前上下文使用过滤器, 
filter代表某个过滤器. 单个上下文可以支持多个过滤器同时使用, 例如:
{{ variable | filter | lower}}在使用的过程中, 有些过滤器还可以传入参数, 但仅支持传入一个参数.
带参数的过滤器与参数之间使用冒号隔开, 并且两者之间不能留有空格, 例如:
{{ variable | date:"D d M Y"}}Django的内置过滤器可以在源码(\django\template\defaultfilters.py)里找到具体的定义过程.
常用内置过滤如表6-3所示.
6-3 内置过滤器
内置过滤器使用形式说明
add`{{valueadd:“2”}}`
addslashes`{{valueaddslashes}}`
capfirst`{{valuecapfirst}}`
cut`{{valuecut:arg}}`
date`{{valuedate:“D d MY”}}`
default`{{valuedefault:“nothing”}}`
default_if_none`{{valuedefault_if_none:“null”}}`
dictsort`{{valuedictsort:“name”}}`
dictsortreversed`{{valuedictsortreversed:“name”}}`
divisibleby`{{valuedivisibleby:arg}}`
escape`{{valueescape}}`
escapejs`{{valueescapejs}}`
filesizeformat`{{valuefilesizeformat}}`
first`{{valuefirst}}`
floatformat`{{valuefloatformat:arg}} {{value
get_digit`{{valueget_digit:“arg”}}`
iriencode`{{valueiriencode}}`
join`{{valuejoin:“arg”}}`
last`{{valuelast}}`
length`{{valuelength}}`
length_is`{{valuelength_is:“arg”}}`
linebreaks`{{valuelinebreaks}}`
linebreaksbr`{{valuelinebreaksbr}}`
linenumbers`{{valuelinenumbers}}`
ljust`{{valueljust}}`
center`{{valuecenter}}`
rjust`{{valuerjust}}`
lower`{{valuelower}}`
make_list`{{valuemake_list}}`
pluralize`{{valuepluralize}} {{value
random`{{valuerandom}}`
removetags`{{valueremovetags:“tag1 tag2 tag3”}}`
safe`{{valuesafe}}`
safeseq`{{valuesafeseq}}`
slice`{{some_listslice:“:2”}}`
slugify`{{valueslugify}}`
striptags`{{valuestriptags}}`
time`{{valuetime}}{{value
truncatewords`{{valuetruncatewords:2}}`
upper`{{valueupper}}`
urlencode`{{valueurlencode}}`
urlize`{{valueurlize}}`
wordcount`{{valuewordcount}}`
wordwrap`{{valuewordwrap:5}}`
timesince`{{valuetimesince:arg}}`
timeuntil`{{valuetimeuntil}}`
使用过滤器的过程中, 上下文, 管道符号'|'和过滤器之间没有规定使用空格隔开, 但为了符合编码的规范性, 建议使用空格隔开.
倘若过滤器需要设置参数, 过滤器, 冒号和参数之间不能有空格, 否则会提示异常信息, 如图6-6所示.

2024-04-15_160518

6-6 异常信息
在实际开发中, 如果内置过滤器的功能不太适合开发需求, 我们可以自定义过滤器来解决问题.
6.1.2小节的MyDjango为例, 在mydefined的templatetags里创建myfilter.py文件, 并在该文件里编写以下代码:
# mydefined\templatetags\mytags.py
from django import template# 创建模板对象
register = template.Library()# 声明并定义过滤器
@register.filter(name='replace')
def do_replace(value, agrs):old_value = agrs.split(':')[0]new_value = agrs.split(':')[1]return value.replace(old_value, new_value)

image-20240415161915672

过滤器与标签的自定义过程有相似之处, 但过滤器的定义过程比标签更简单, 只需定义相关函数即可.
上述定义的过滤器是实现模板上下文的字符替换, 定义过程说明如下:
 函数do_replace由装饰器register.filter(name='replace')处理, 对函数执行过滤器注册操作.
 装饰器参数name用于为过滤器命名, 如果没有设置参数name, 就以函数名作为过滤器名.函数名没有具体要求, 一般以'do_过滤器名称''过滤器名称'作为命名规范.
 参数value代表使用当前过滤器的模板上下文, 参数agrs代表过滤器的参数.函数将参数agrs以冒号进行分割, 用于参数value(模板上下文)进行字符串替换操作,函数必须将处理结果返回, 否则在使用过程中会出现异常信息.
为了验证自定义过滤器replace的功能, 将index的views.py和模板文件index.html的代码进行修改:
# index的views.py
from django.shortcuts import render
def index(request):value = 'Hello Python'return render(request, 'index.html', locals())

image-20240415161944511

<!-- templates的index.html -->
{#导入自定义过滤器文件myfilter#}
{% load myfilter %}
<!DOCTYPE html>
<html lang="en">
<head><title></title><meta charset="UTF-8">
</head>
<body>
<div>替换前: {{ value }}</div>
<br>
<div>替换后:{{ value | replace:'Python:Django' }}
</div>
</body>
</html>

image-20240415162133712

模板文件index.html使用自定义过滤器时, 需要使用{% load myfilter %}导入过滤器文件,
这样模板引擎才能找到自定义过滤器, 否则会提示TemplateSyntaxError异常.
过滤器replace将模板上下文value进行字符串替., 将value里面的Python替换成Django, 运行结果如图6-7所示.

image-20240415161834829

6-7 运行结果

6.2 Jinja2模板引擎

Jinja2是Python里面被广泛应用的模板引擎, 它的设计思想来源于Django的模板引擎, 并扩展了其语法和一系列强大的功能.
其中最显著的是增加了沙箱执行功能和可选的自动转义功能, 这对大多数应用的安全性来说是非常重要.此外, 它还具备以下特性:
 沙箱执行模式, 模板的每个部分都在引擎的监督之下执行, 模板将会被明确地标记在白名单或黑名单内, 这样对于那些不信任的模板也可以执行.
 强大的自动HTML转义系统, 可以有效地阻止跨站脚本攻击.
 模板继承机制, 此机制可以使得所有模板具有相似一致的布局, 也方便开发人员对模板进行修改和管理.
 高效的执行效率, Jinja2引擎在模板第一次加载时就把源码转换成Python字节码, 加快模板执行时间.
 调试系统融合了标准的Python的TrackBack功能, 使得模板编译和运行期间的错误能及时被发现和调试.
 语法配置, 可以重新配置Jinja2, 使得它更好地适应LaTeX或JavaScript的输出.
 官方文档手册, 此手册指导设计人员更好地使用Jinja2引擎的各种方法.Django支持Jinja2模板引擎的使用, 由于Jinja2的设计思想来源于Django的模板引擎,
因此Jinja2的使用方法与Django的模板语法有相似之处.

6.2.1 安装与配置

Jinja2支持pip指令安装, 我们按快捷键Windows+R打开'运行'对话框, 然后在对话框中输入'CMD'并按回车键, 进入命令提示符(也称为终端).
在命令提示符下输入以下安装指令: pip install Jinja2
(使用这个: pip install jinja2 -i https://pypi.tuna.tsinghua.edu.cn/simple 清华源下载,下载最新版本, 老版本不我使用的时候不兼容了...)
输入上述指令后按回车键, 就会自行下载Jinja2最新版本并安装, 我们只需等待安装完成即可.
除了使用pip安装之外, 还可以从网上下载Jinja2的压缩包自行安装.
在浏览器上输入下载网址(www.lfd.uci.edu/~gohlke/pythonlibs/#sendkeys)并找到Jinja2的下载链接, 
如图6-8所示(地址失效, 图片最废).
然后将下载的文件放到D盘, 并打开命令提示符窗口, 输入以下安装指令:
pip install D:\Jinja2?2.10?py2.py3?none?any.whl
输入指令后按回车键, 等待安装完成的提示即可.
完成Jinja2的安装后, 需要进一步校验安装是否成功, 再次进入命令提示符窗口, 
输入'python'并按回车键, 此时进入Python交互解释器, 在交互解释器下输入校验代码:
>>> import jinja2
>>> jinja2.__version__
'3.1.3'

image-20240415165518921

Jinja2安装成功后, 接着在Django里配置Jinja2模板.
由于Django的内置功能是使用Django的模板引擎, 如果将整个项目都改为Jinja2模板引擎, 就会导致内置功能无法正常使用.
在这种情况下, 既要保证内置功能能够正常使用, 又要使用Jinja2模板引擎, 只能将两个模板引擎共存在同一个项目里.
以MyDjango为例, 在MyDjango文件夹里创建jinja2.py文件,
文件名没有固定的命名要求, 读者可以自行命名, 该文件的作用是将Jinja2模板加载到MyDjango项目, 项目的目录结构如图6-9所示.

image-20240415171514033

6-9 目录结构
在PyCharm里打开jinja2.py文件, 在文件里定义函数environment, 
并在函数里使用Jinja2的类Environment进行实例化, 实例化对象env用于对接Django的运行环境. 文件代码如下:
from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse
from jinja2 import Environment# 将jinja2模板设置到项目环境
def environment(**options):env = Environment(**options)env.globals.update({'static': staticfiles_storage.url,'url': reverse})return env
下一步将jinja2.py文件定义的函数environment写到配置文件settings.py中,
否则jinja2.py文件所定义的函数无法作用在MyDjango项目里.
在配置属性TEMPLATES中新增Jinja2模板引擎, 代码如下:
TEMPLATES = [# 使用Jinja2模板引擎{'BACKEND': 'django.template.backends.jinja2.Jinja2','DIRS': [BASE_DIR / 'templates'],'APP_DIRS': True,'OPTIONS': {'environment': 'MyDjango.jinja2.environment'}},# 使用Django的模板引擎(默认引擎){'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [],  # 设置为空列表'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',],},},
]

image-20240415172349279

配置属性TEMPLATES是以列表形式表示的, 列表里定义了两个元素, 每个元素是以字典形式表示的, 说明如下:
 第一个列表元素设置Jinja2模板引擎, 属性OPTIONS的environment是MyDjango文件夹的jinja2.py文件所定义的函数environment,并且属性DIRS指向项目里的模板文件夹templates, 这说明模板文件夹templates里的所有模板文件皆由Jinja2模板引擎执行解析处理.
 第二个列表元素设置Django的模板引擎, 属性OPTIONS的context_processors代表Django的内置功能,如Admin后台系统, 信息提示和认证系统等, 也就是说Django的内置功能所使用的模板还是由Django的模板引擎执行解析处理的.完成项目环境配置后, 通过简单的示例来验证MyDjango项目是否能同时使用内置模板引擎和Jinja2模板引擎.
在MyDjango的urls.pyindex的urls.py, views.py和模板文件index.html中编写以下代码:
# MyDjango 的 urls.py
from django.contrib import admin
from django.urls import path, includeurlpatterns = [path('admin/', admin.site.urls),path('', include(('index.urls', 'index'), namespace='index'))
]
# index 的 urls.py
from django.urls import path
from .views import *
urlpatterns = [# 定义路由path('', index, name='index'),
]
# index 的 views.py
from django.shortcuts import renderdef index(request):value = {'name': 'This is Jinja2'}return render(request, 'index.html', locals())

image-20240415173156889

<!-- templates 的 index.html -->
<!DOCTYPE html>
<html lang="en">
<head><title>Jinja2</title><meta charset="UTF-8">
</head>
<body>
<div>{{ value['name'] }}</div>
</body>
</html>

2024-04-15_173236

看不舒服可以修改模板语言为Jinja2.
File --> Settings --> Languages & Frameworks -->  Template Language --> 选择Jinja2 --> OK.

2024-04-15_182743

模板文件index.html的上下文value是字典对象, 而value['name']用于获取字典的属性name, 
这种获取方式是Jinja2特有的模板语法, Django内置模板引擎是不支持的.运行MyDjango项目, 分别访问: 127.0.0.1:8000  127.0.0.1:8000/admin, 发现两者都能成功访问.
网站首页是由Jinja2模板引擎解析的, 而Admin后台系统是由Django内置模板引擎解析的, 如图6-10所示.

image-20240415173843814

6-10 运行结果

6.2.2 模板语法

尽管Jinja2的设计思想来源于Django的模板引擎, 但在功能和使用细节上, Jinja2比Django的模板引擎更为完善,
而且Jinja2的模板语法在使用上与Django的模板引擎存在一定的差异.由于Jinja2有模板设计人员帮助手册(官方文档: https://jinja.palletsprojects.com/en/3.0.x/)
并且官方文档对模板语法的使用说明较为详细, 因此这里只讲述Jinja3与Django模板语言的使用差异.
6.2.1小节的MyDjango项目为例, 在模板文件夹templates里创建新的模板文件base.html, 该文件用于模板继承;
然后在根目录下创建文件夹static, 并在该文件夹里放置favicon.ico图片, 
新建的文件夹static必须在settings.py中配置STATICFILES_DIRS, 否则Django无法识别文件夹static的静态资源;

image-20240415193351152

# MyDjango 的 settings.py
STATIC_URL = '/static/'
# 资源集合
STATICFILES_DIRS = [# 设置根目录的静态资源文件夹staticBASE_DIR / 'static',
]

image-20240415182148245

最后分别在模板文件base.html和index.html中编写以下代码:
<!-- templates 的 base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
{% block title %}<title>标题</title>
{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>

image-20240415193635921

<!-- templates 的 index.html -->
{# 模板继承 #}
{% extends 'base.html' %}{% block title %}{# static 标签 #}{# Django 的用法: {% static 'favicon.png' %} #}<link rel="icon" href="{{ static('favicon.ico') }}"><title>Jinja2</title>
{% endblock %}{% block body %}<link rel="icon" href="{{ static('favicon.ioc') }}">{# 使用上下文 #}{# Django 的用法 {{ value.name }} #}{# Jinja2 除了支持Django的用法, 还支持以下用法 #}<div>{{ value['name'] }}</div>{# 使用过滤器 #}<div>{{ value['name'] | replace('Jinja2', 'Django') }}</div>{# for 循环 #}{# Django的用法 {% for k, v in value.items  %} #}{% for k, v in value.items() %}<div> key is {{ k }} </div><div> value is {{ v }}</div>{% endfor %}{# if 判断 #}{% if value %}<div>This is if</div>{% else %}<div>This is else</div>{% endif %}{# url 标签 #}{# Django 的用法: {% url 'index:index' %} #}<a href="{{ url('index:index') }}">首页</a>
{% endblock %}

image-20240415193708563

从上述代码得知, Jinja2与Django模板语法的最大差异在于static函数, url函数和过滤器的使用方式,
而模板继承这一功能上, 两者的使用方式是相同的, 对于Jinja2来说, 它没有模板标签这一概念.
在for循环中, Jinja2提供了一些特殊变量来获取循环信息, 变量说明如表6-4所示.运行项目, 输入地址: http://127.0.0.1:8000/ .

image-20240415193754386

变量描述
loop.index循环的当前迭代(索引从1开始)
loop.index0循环的当前迭代(索引从0开始)
loop.revindex循环结束时的迭代次数(索引从1开始)
loop.revindex0循环结束时的迭代次数(索引从0开始)
loop.first如果是第一次迭代, 就为True
loop.last如果是最后一次迭代, 就为True
loop.length序列中的项目数, 即循环总次数
loop.cycle辅助函数, 用于在序列列表之间循环
loop.depth当前递归循环的深度, 从1级开始
loop.depth0当前递归循环的深度, 从0级开始
loop.previtem上一次迭代中的对象
loop.nextitem下一次迭代中的对象
loop.changed(value)若上次迭代的值与当前迭代的值不同, 则返回True
Jinja2的过滤器与Django内置过滤器的使用方法有相似之处, 也是由管道符号'|'连接模板上下文和过滤器,
但是两者的过滤器名称是不同的, 而且过滤器的参数设置方式也不同.
我们以表格的形式列举Jinja2的常用过滤器, 如表6-5所示.
过滤器使用方式说明
abs`{{ valueabs }}`
default`{{ valuedefault(‘new’) }}`
escape`{{ valueescape }}`
first`{{ valuefirst }}`
last`{{ valuelast }}`
length`{{ valuelength }}`
join`{{ valuejoin(‘-’) }}`
safe`{{ valuesafe }}`
int`{{ valueint }}`
float`{{ valuefloat }}`
lower`{{ valuelower }}`
upper`{{ valueupper }}`
replace`{{ valuereplace(‘a’,‘b’) }}`
truncate`{{ valuetruncate(9,true) }}`
striptags`{{ valuestriptags }}`
trim`{{ valuetrim }}`
string`{{ valuestring }}`
wordcount`{{ valuewordcount }}`
6-5 常用过滤器

6.2.3 自定义过滤器

Jinja2支持开发者自定义过滤器, 而且过滤器的自定义过程比Django内置模板更为便捷, 只需将函数注册到Jinja2模板对象即可.
我们以6.2.1小节的MyDjango为例, 在MyDjango文件夹的jinja2.py里定义函数myReplace,
并将函数注册到Jinja2的环境函数environment里, 这样就能完成过滤器的自定义过程.
jinja2.py的代码如下:
from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse
from jinja2 import Environment# 自定义过滤器
def my_replace(value, old='Jinja2', new='Django'):return str(value).replace(old, new)# 将jinja2模板设置到项目环境
def environment(**options):env = Environment(**options)env.globals.update({'static': staticfiles_storage.url,'url': reverse})# 注册自定义过滤器my_replaceenv.filters['my_replace'] = my_replacereturn env

image-20240415211239684

函数myReplace一共设置了3个参数, 每个参数说明如下:
 参数value是过滤器的必选参数, 参数名可自行命名, 代表模板上下文.
 参数old是过滤器的可选参数, 参数名可自行命名, 代表过滤器的第一个参数.
 参数new是过滤器的可选参数, 参数名可自行命名, 代表过滤器的第二个参数.函数environment将Jinja2引擎对接Django的运行环境, 它由实例化对象env实现对接过程,
在创建实例化对象env时, 只需在对象env中使用filters方法即可将自定义过滤器注册到Jinja2引擎.
为了验证自定义过滤器myReplace的功能, 我们在模板文件index.html里使用过滤器myReplace, 代码如下:
<!-- templates的index.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title></title>
</head>
<body>
<div>{{ value.name | myReplace }}
</div>
<div>{{ value.name | myReplace('This','That') }}
</div>
</body>
</html>

image-20240415211319858

如果过滤器myReplace没有设置参数old和new, 
就默认将字符串的'Jinja2'替换成'Django', 如果设置参数old和new, 就以设置的参数作为替换条件.
运行MyDjango项目, 在浏览器访问127.0.0.1:8000, 运行结果如图6-11所示.

image-20240415211157967

6-11 运行结果

6.3 本章小结

Django内置模板引擎包含模板上下文(亦可称为模板变量), 标签和过滤器, 各个功能说明如下:
 模板上下文是以变量的形式写入模板文件里面的, 变量值由视图函数或视图类传递所得.
 标签是对模板上下文进行控制输出, 比如模板上下文的判断和循环控制等.
 模板继承隶属于标签, 它是将每个模板文件重复的代码抽取出来并写在一个共用的模板文件中,其他模板文件通过继承共用模板文件来实现完整的网页输出.
 过滤器是对模板上下文进行操作处理, 比如模板上下文的内容截取, 替换或格式转换等.Jinja2是Python里面被广泛应用的模板引擎, 它的设计思想来源于Django的模板引擎, 并扩展了其语法和一系列强大的功能.
其中最显著的是增加了沙箱执行功能和可选的自动转义功能, 这对大多数应用的安全性来说非常重要.
此外, 它还具备以下特性:
 沙箱执行模式, 模板的每个部分都在引擎的监督之下执行, 模板将会被明确地标记在白名单或黑名单内, 这样对于那些不信任的模板也可以执行.
 强大的自动HTML转义系统, 可以有效地阻止跨站脚本攻击.
 模板继承机制, 此机制可以使得所有模板都具有相似一致的布局, 也方便开发人员对模板进行修改和管理.
 高效的执行效率, Jinja2引擎在模板第一次加载时就把源码转换成Python字节码, 加快模板执行时间.
 调试系统融合了标准的Python的TrackBack功能, 使得模板编译和运行期间的错误能及时被发现和调试.
 语法配置, 可以重新配置Jinja2, 使得它更好地适应LaTeX或JavaScript的输出.
 官方文档手册, 此手册指导设计人员更好地使用Jinja2引擎的各种方法.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/818579.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

项目7-音乐播放器1+BCrypt加密

1.创建项目 1.1 引入依赖 1.2 yml相关配置 application.yml spring:profiles:active: prod mybatis:mapper-locations: classpath:mapper/**Mapper.xmlconfiguration:map-underscore-to-camel-case: true #配置驼峰⾃动转换log-impl: org.apache.ibatis.logging.stdout.StdO…

openGauss学习笔记-261 openGauss性能调优-使用Plan Hint进行调优-将部分Error降级为Warning的Hint

文章目录 openGauss学习笔记-261 openGauss性能调优-使用Plan Hint进行调优-将部分Error降级为Warning的Hint261.1 功能描述261.2 语法格式261.3 示例261.3.1 忽略非空约束261.3.2 忽略唯一约束261.3.3 忽略分区表无法匹配到合法分区261.3.4 更新/插入值向目标列类型转换失败 o…

【算法】数组元素循环右移k位,并要求只用一个元素大小的附加存储,元素移动或交换次数为O(n)

两种写法思路&#xff1a; 思路一&#xff1a;三次倒置 前言&#xff1a;C/C函数 reverse 是 左闭右开区间的&#xff0c;作用是将指定范围数组元素全部倒置&#xff0c;数组从 0 开始&#xff0c;这里主要讲解思路&#xff0c;就直接用 函数 reverse 简化过程 这个方法 实现 …

vue3第十八节(diff算法)

引言&#xff1a; 上一节说了key的用途&#xff0c;而这个key属性&#xff0c;在vue的vnode 中至关重要&#xff0c;直接影响了虚拟DOM的更新机制&#xff1b; 什么场景中会用到diff算法 如&#xff1a;修改响应式属性需要重新渲染页面&#xff0c;会重新执行render渲染函数返…

为了执行SQL语句,MySQL的架构是怎样设计的

1. 把MySQL当个黑盒子一样执行SQL语句 上一讲我们已经说到&#xff0c;我们的系统采用数据库连接池的方式去并发访问数据库&#xff0c;然后数据库自己其实也会维护一个连 接池&#xff0c;其中管理了各种系统跟这台数据库服务器建立的所有连接 我们先看下图回顾一下 当我们的…

数据可视化-ECharts Html项目实战(12)

在之前的文章中&#xff0c;我们深入学习ECharts特殊图表中的矩形树图以及Echarts中高级功能的多图表联动。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 数…

Directory Monitor:全方位监控文件系统变动的专业利器

目录 一、软件介绍 二、软件功能 三、软件特点 四、安装说明 五、使用说明 一、软件介绍 Directory Monitor是一款强大易用的实时文件系统监视工具&#xff0c;它由Michael Humpa开发&#xff0c;专为满足用户监控特定目录下文件和子目录变化的需求。无论是为了保障系统安…

python与设计模式之工厂模式的那些事儿

一、工厂模式 工厂模式实现了按需创建的最佳模式&#xff0c;其目的是为了隐藏创建类的细节与过程&#xff0c;通过一个统一的接口来创建所需的对象。 话说没了皇位争夺权的皇三接到了一个外征的工作&#xff0c;始皇给了5个亿的经费让皇三组建一个军队。打权总是要进行武器采…

【Java开发指南 | 第二篇】标识符、Java关键字及注释

读者可订阅专栏&#xff1a;Java开发指南 |【CSDN秋说】 文章目录 标识符Java关键字Java注释 标识符 Java 所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。 所有的标识符都应该以字母&#xff08;A-Z 或者 a-z&#xff09;,美元符&#xff08;$&#xff0…

CentOS 7安装、卸载MySQL数据库

说明&#xff1a;本文介绍如何在CentOS 7操作系统下使用yum方式安装MySQL数据库&#xff0c;及卸载&#xff1b; 安装 Step1&#xff1a;卸载mariadb 敲下面的命令&#xff0c;查看系统mariadb软件包 rpm -qa|grep mariadb跳出mariadb软件包信息后&#xff0c;敲下面的命令…

【Qt】:事件的处理

系统相关 一.鼠标事件二.键盘事件三.定时器 事件是应用程序内部或者外部产生的事情或者动作的统称。在Qt中使用一个对象来表示一个事件。所有的Qt事件均继承于抽象类QEvent。事件是由系统或者Qt平台本身在个同的的刻友出的。当用广投下鼠标、敲下键盘&#xff0c;或者是窗口需要…

第四百六十二回

文章目录 1. 概念介绍2. 实现方法3. 示例代码4. 内容总结 我们在上一章回中介绍了"关于MediaQuery的优化"相关的内容&#xff0c;本章回中将介绍readMore这个三方包.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本章回中介绍的readMore是一个…

【团体程序设计天梯赛 往年关键真题 25分题合集 详细分析完整AC代码】(L2-001 - L2-024)搞懂了赛场上拿下就稳了

L2-001 紧急救援 最短路路径打印 样例 输入1 4 5 0 3 20 30 40 10 0 1 1 1 3 2 0 3 3 0 2 2 2 3 2输出1 2 60 0 1 3分析 用一遍dijkstra算法。设立 n u m [ i ] num[i] num[i]和 w [ i ] w[i] w[i]表示从出发点到i结点拥有的路的条数&#xff0c;以及能够找到的救援队的数目…

Websocket (帧格式, 握手过程, Spring 中使用 WebScoket 协议)

什么是 WebSocket 客户端 A 和客户端 B 的消息传播需要借助服务器的中转 (原因是内网不能给另一个局域网的内网直接联通, 需要借助服务器的外网做 “中介”) (NAT 地址转换) Http 协议 不支持实时通讯 (或者说不支持服务器主动推送数据给客户端) TCP 本身是具有服务器推送数据这…

【verilog】 reg与寄存器的关系

一、前言 在Verilog中经常用reg定义具有数据寄存功能的单元&#xff0c;但在verilog的使用中&#xff0c;并不代表其一定就是寄存单元&#xff0c;reg还能进行组合逻辑描述&#xff0c;并且在一些场景下&#xff0c;只能使用reg来申明变量。 二、reg型变量生成组合逻辑 在Ve…

linux shell脚本编写(2)

Shell: 命令转换器&#xff0c;高级语言转换成二进制语言。是Linux的一个外壳&#xff0c;它包在Lniux内核的外面&#xff0c;用户和内核之间的交互提供了一个接口。 内置命令&#xff1a;在shell内部不需要shell编辑 外置命令&#xff1a;高级语言要用shell转换成二进制语言 …

(一)Jetpack Compose 从入门到会写

基本概念 Compose 名称由来 众所周知&#xff0c;继承在功能拓展上表现的很脆弱&#xff0c;容易类、函数爆炸&#xff0c;通过代理和包装进行组合会更健壮。 Compose 意为组合&#xff0c;使用上也是把 Compose 函数以 模拟函数调用层级关系的方式 组合到一起&#xff0c;最终…

PCL中VTK场景添加坐标系轴显示

引言 世上本没有坐标系&#xff0c;用的人多了&#xff0c;便定义了坐标系统用来定位。地理坐标系统用于定位地球上的位置&#xff0c;PCL点云库可视化窗口中的坐标系统用于定位其三维世界中的位置。本人刚开始接触学习PCL点云库&#xff0c;计算机图形学基础为零&#xff0c;…

基于Python的卷积网络的车牌识别系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

leetcode不同路径

. - 力扣&#xff08;LeetCode&#xff09; 62. 不同路径 中等 相关标签 相关企业 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下…