『Django』模板
本文简介
点赞 + 关注 + 收藏 = 学会了
上一篇文章介绍了 Django 视图的基础用法,当时提到了“响应HTML
模版”,用到的方式是渲染一段 HTML
内容的字符串,这种方式很不方便。更方便的方法是在 .html
文件里写页面内容,然后渲染这个 .html
文件。这个 .html
文件也叫 HTML
模版,就是本文要讲解的内容。
什么是模板?
简单来说,在 Django
的模板就是一个“升级版”的 HTML
文件。
我们使用 Vue
、React
这些流行的前端框架时也会用到模板,它们的用法其实和 Django
里的模板用法也很像。
举个例子。
<div>{{ msg }}</div>
在这段 HTML
代码中有一串 {{ msg }}
这样的代码,这是 Django
模板的语法,它能将 msg
这个变量的值加载到 <div>
标签里。
这种模版最大的好处就是复用。想象一下,你有一个网站,需要在每个页面上显示相同的信息,比如标题、导航栏和页脚。模板就是用来帮你做这些的,它们让你可以创建一个模板文件,里面包含网站的基本布局和内容,然后在每个页面中使用这个模板来展示你的信息。这样,你就不必每次都重新写一遍相同的代码,而是可以简单地重复使用模板,节省时间和精力。
Django
提供了2个模板引擎分别是 DTL (Django Template Language) 和 Jinja2。同时 Django
还支持使用第三方模板,但这不是本文要讲的内容。本文主要介绍 DTL。
当然啦,现在还流行前后端分离,工作中可能用到后端模板的机会变少了。但如果你想自己搞点产品出来,又懒得前后端分离,后端模板还是一个不错的选择。现在有些企业官网也仍然使用后端模板来编写的。
配置模板
使用 Django
模板之前,需要配置一下模版的路径(位置)。
通常会在项目根目录创建一个 templates
文件夹用来存放模版(.html
文件),然后在项目配置文件 settings.py
里指定 templates
文件夹的路径。
在 settings.py
里的 TEMPLATES
里第一个元素的 DIRS
字段配置一下 'templates'
,这样就指定了模版路径为项目根目录下的 templates
了。
APP_DIRS
这个字段也设置为 True
,这样就允许在应用中配置模版。如果忘了什么是“应用”可以回顾一下 《『Django』创建app(应用程序)》。
模板的基础用法
全局模板
配置好模板路径就可以使用模板了。
首先在 templates
目录里创建 blog.html
文件,内容如下:
<!-- blog.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>博客</title>
</head>
<body>
<div>雷猴,这是博客首页~</div>
</body>
</html>
然后在项目的 urls.py
配置路由。
# urls.py
from django.urls import path
from blog.views import blogIndex
urlpatterns = [
path("blog/", blogIndex)
]
接着在blog应用里使用这个模板。
# blog/views.py
from django.http import HttpResponse
from django.shortcuts import render
def blogIndex(request):
return render(request, 'blog.html')
最后运行以下命令,并在浏览器 http://127.0.0.1:8000/blog/
python3 manage.py runserver
注意看,在 blog/views.py
是直接使用 return render(request, 'blog.html')
,直接使用模板的,并没有引入它的代码。这是因为在 settings.py
里配置过模板的路径,所以直接使用模板时会先在配置好的路径里找。
还有还有,在视图 views.py
里使用 render
方法,它可以直接渲染 html
文件。
应用模板
删掉项目里的 templates
文件夹。
然后在 settings.py
中,把 TEMPLATES
里的 APP_DIRS
设置为 True
后,再在 blog
应用中创建 templates
文件夹,里面放入 blog.html
文件,内容如下:
<!-- blog/templates/blog.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>博客</title>
</head>
<body>
<div>雷猴,这是blo应用内的博客页面~~~</div>
</body>
</html>
为了和前面的“全局模板”的内容区分一下,这个文件的内容稍微有点不同。
接着在浏览器访问 http://127.0.0.1:8000/blog/
会发现报错了。
如果需要使用应用内的模板还需要多配置一项。
在项目配置文件 settings.py
的 INSTALLED_APPS
里加上你的应用名。
# 省略部分代码
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"blog"
]
这里我把 blog
这个应用加上了。接着重新打开 http://127.0.0.1:8000/blog/
就能看到应用内的模板内容了。
语法
Django
支持在模板中插入变量,支持使用条件判断、循环等功能。接下来介绍一些常用的模板能力。
标签
Django
模板的标签是一种特殊的语法,用于在模板中执行逻辑操作和控制模板的渲染行为。这些标签由一对花括号 {{ }}
或百分号 {% %}
包裹,以便与模板中的普通文本区分开来。
主要有两种类型的 Django
模板标签:
- 变量标签:用双花括号
{{ }}
包裹,用于在模板中输出变量的值,例如{{ variable }}
。 - 控制标签:用百分号
{% %}
包裹,用于执行逻辑操作,如循环、条件判断等,例如{% if condition %} ... {% endif %}
。有些控制标签由“开始标签”和“结束标签”组合而成。
接下来逐一讲解。
变量
首先要介绍的是变量。在模板中变量需要使用两对花括号 {{}}
包裹起来。
基本语法:
{{ 变量名 }}
Django
模板变量的用法和 Vue
是一样的。
那这个变量是从哪里传过来的呢?
通常流程是在视图 views.py
把处理好的数据传入模板里。
举个例子:
# blog/views.py
from django.http import HttpResponse
from django.shortcuts import render
def blogIndex(request):
msg = '雷猴'
return render(
request,
'blog.html',
{
"msg": msg
}
)
然后在模板中直接使用:
<!-- blog/templates/blog.html -->
<!-- 省略部分代码 -->
<body>
<div>{{ msg }}</div>
</body>
打开 http://127.0.0.1:8000/blog/
瞧瞧
条件判断 if
条件判断是一种常用的操作,用于根据特定条件的真假来决定模板中的内容渲染方式。Django
模板提供了 {% if %}
和 {% endif %}
标签来实现条件判断。
需要注意,结尾必须用 {% endif %}
表示条件判断结束了。
当有多个条件判断时,可以使用 {% if %}
标签配合 {% elif %}
和 {% else %}
标签。这样可以在多个条件之间进行逐一检查,直到找到第一个满足条件的分支。
基本语法:
{% if condition1 %}
... # 条件1成立时执行的内容
{% elif condition2 %}
... # 条件1不成立,条件2成立时执行的内容
{% elif condition3 %}
... # 条件1和条件2都不成立,条件3成立时执行的内容
{% else %}
... # 所有条件都不成立时执行的内容
{% endif %}
举个例子,将文章可见性的数值转换成中文。
在视图里设定一个文章可见性的变量 visibility
,将它传给模版。因为本文主要介绍模版的用法
# blog/views.py
def blogIndex(request):
visibility = 2
return render(request, 'blog.html', {
"visibility": visibility
})
在模板中接收
<!-- blog/templates/blog.html -->
<div>
{% if visibility == 0 %}
<div>仅自己可见</div>
{% elif visibility == 1 %}
<div>VIP用户可见</div>
{% elif visibility == 2 %}
<div>粉丝可见</div>
{% else %}
<div>无限制</div>
{% endif %}
</div>
在浏览器打开 http://127.0.0.1:8000/blog/
循环渲染 for
当需要渲染一个列表时,可以使用 for
循环将其输出到页面中。
基本语法:
{% for item in list %}
... # 渲染每个元素的内容
{% endfor %}
举个例子,我要将我所有专栏的名称都渲染出来。
# blog/views.py
def blogIndex(request):
list = [
'写给前端的Django教程',
'AIGC',
'读书破万卷',
'Python',
'产品汪',
'乱up24',
'新手村',
'艺术系必修课:P5.js',
'SVG',
'canvas',
'Three.js',
'零基础入门Fabric.js,提高Canvas开发效率',
'数据可视化'
]
return render(request, 'blog.html', {
"list": list
})
在模版中遍历出来。
<!-- blog/templates/blog.html -->
<ul>
{% for item in list %}
<li>{{ item }}</li>
{% endfor %}
</ul>
循环使用一组值 cycle
关于循环,Django
模板还提供了一个叫 cycle
的功能。
在前端,热门的UI框架的表格组件一般都提供“斑马纹”的样式,比如奇数行使用白色背景,偶数行使用浅灰色背景。
Django
模板的 cycle
可以很方便的实现这个功能。
基本语法:
{% cycle 'value1' 'value2' ... as cycle_name %}
举个简单的例子(为了方便,这里使用 ul
而不是 table
)。
# blog/views.py
def blogIndex(request):
list = [
'写给前端的Django教程',
'AIGC',
'读书破万卷',
'Python',
'产品汪',
'乱up24',
'新手村',
'艺术系必修课:P5.js',
'SVG',
'canvas',
'Three.js',
'零基础入门Fabric.js,提高Canvas开发效率',
'数据可视化'
]
return render(request, 'blog.html', {
"list": list
})
模版代码:
<!-- blog/templates/blog.html -->
<style>
.row1 {
background: white;
}
.row2 {
background: gainsboro;
}
</style>
<ul>
{% for item in list %}
<li class="{% cycle 'row1' 'row2' %}">{{ item }}</li>
{% endfor %}
</ul>
自动转义 autoescape
Django
默认会将 HTML
代码进行转义。自动转义功能可以确保在渲染模板时,将 HTML
标签和特殊字符(如 <
, >
, &
, '
, "
等)转义为相应的HTML实体(如 <
, >
, &
, '
, "
),从而避免用户提供的数据被误解释为 HTML
标签或 JavaScript
代码,导致安全漏洞,以防止跨站点脚本攻击(XSS)。
比如,我想在页面输出一个超链接,使用 <a>
标签来实现。
# blog/views.py
def blogIndex(request):
django_column = '<a href="https://juejin.cn/column/7355677638881706019">写给前端的Django教程</a>'
return render(request, 'blog.html', {
"django_column": django_column
})
在模板中渲染出来。
<!-- blog/templates/blog.html -->
<div>
{{ django_column }}
</div>
出来的效果是这样的,明显不是我想要的。
如果此时想将 <a>
标签这段字符串转换成 HTML
元素来渲染,而不是输出字符串的话,可以使用 autoescape
。
基本语法:
{% autoescape on %}
<p>{{ user_input }}</p> <!-- user_input会被自动转义 -->
{% endautoescape %}
{% autoescape off %}
<p>{{ user_input }}</p> <!-- user_input不会被自动转义 -->
{% endautoescape %}
注意,在开始的 autoescape
后面会跟上 on
或者 off
,默认值是 on
,也就是说你不写 {% autoescape on %}
这段,默认情况也会按 {% autoescape on %}
来渲染。
on
:表示自动转义off
:表示不自动转义
此时我想将 '<a href="https://juejin.cn/column/7355677638881706019">写给前端的Django教程</a>'
这段字符串渲染成 HTML
元素,就要将 autoescape
设置为 off
。
<!-- blog/templates/blog.html -->
<div>
{% autoescape off %}
{{ django_column }}
{% endautoescape %}
</div>
过滤器
过滤器可以让你在模板中对数据进行转换和修改, Django
的过滤器语法和 Vue 2
的过滤器语法一样。
基本语法:
{{ 变量 | 过滤器名称 }}
常用的过滤器有以下这些(为了方便演示,下面的例子不再罗列视图的代码)。
转大/小写:upper / lower
使用 upper
可以将英文字母都转成大写,用 lower
可以转成小写。但这两个过滤器都无法处理中文。
视图传来的值:msg = 'Abc'
<!-- blog/templates/blog.html -->
<div>{{ msg | upper }}</div>
<div>{{ msg | lower }}</div>
字符串转slug格式:slugify
slug
过滤器可以将“Hello World”变成“hello-world”,但它无法处理中文。
视图传来的值:msg = 'Hello World'
<!-- blog/templates/blog.html -->
<div>{{ msg | slug }}</div>
默认值:default
当视图传过来的值是空,那就使用默认值。
视图传来的值:gender = ''
<!-- blog/templates/blog.html -->
<div>{{ gender | default:'未知' }}</div>
字符串长度:length
返回字符串的长度。
视图传来的值:msg = '雷猴'
<!-- blog/templates/blog.html -->
<div>{{ msg | length }}</div>
字符串删除指定字符:cut
将指定的字符串删除掉,比如这个例子中要删除所有 txt
。
视图传来的值:msg = 'txt雷猴txt'
<!-- blog/templates/blog.html -->
<div>{{ msg | cut:"txt" }}</div>
超出长度用省略号表示:truncatechars
将超出指定数量的字符串用省略号表示。比如限制了输出10个字符,后面的都用省略号表示。
视图传来的值:msg = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
<!-- blog/templates/blog.html -->
<div>{{ msg | truncatechars:10 }}</div>
列表转字符串:join
将列表转换为指定内容分隔的字符串。例如,我想让列表转换成使用 -/-
这3个字符串分割的字符串。
视图传来的值:list = ['雷猴', '鲨鱼辣椒', '蝎子莱莱', '蟑螂恶霸']
<!-- blog/templates/blog.html -->
<div>{{ list | join:"-/-" }}</div>
返回列表中的第一个或最后一个元素: first、
last
视图传来的值:list = ['雷猴', '鲨鱼辣椒', '蝎子莱莱', '蟑螂恶霸']
<!-- blog/templates/blog.html -->
<div>{{ list | first }}</div>
<div>{{ list | last }}</div>
格式化浮点数:floatformat
让浮点数保留多少位小数,可以用 floatformat
过滤器来过滤,它会四舍五入保留指定位小数。
视图传来的值:num = 12.009
<!-- blog/templates/blog.html -->
<div>{{ num | floatformat:"2" }}</div>
注释
注释分单行注释和多行注释。
单行注释:
{# 这里是被注释的内容 #}
多行注释:
{% comment %}
这里面的内容都会被注释掉
这里面的内容都会被注释掉
这里面的内容都会被注释掉
{% endcomment %}
多行注释需要使用 comment
来实现。
加载静态资源
这里指的静态资源文件包括图片、css文件、js文件等。
当我们想将公共的样式写在一个 css
文件里,或者有一些公共的 js
方法要单独放在一个 js
文件里,又或者要在页面加载一张存放在项目里的图片时,可以用以下方法配置。
首先在项目根目录中创建一个 static 文件夹,然后把静态资源放进去。
然后在项目配置文件 settings.py
里配置静态资源路径。
# demo1/settings.py
import os
STATIC_URL = 'static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
STATIC_URL
是一个Django设置,用于指定在模板中引用静态文件时的基础URL。
STATICFILES_DIRS
用于指定额外的静态文件目录,所以要引入 os
模块。
然后在 html
中,需要在页面第一行写上 {% load static %}
,作用是用于加载静态文件模板标签库。加载静态文件模板标签库后,模板就可以使用静态文件相关的模板标签,例如{% static %}
标签。
比如我要在页面中引入 static/images/raccoon.jpg
这张图片。
<!-- blog/templates/blog.html -->
{% load static %}
<img src="{% static '/images/raccoon.jpg' %}" alt="">
要引入 css
和 js
也同理,settings.py
文件的配置跟前面配置的一样。
{% load static %}
<head>
<link rel="stylesheet" href="{% static '/css/style.css' %}">
</head>
模板继承
对于相同的内容,我们就可以使用一个父模版,然后中间不同的地方用相应的内容进行替换。
举个例子,我在 blog
应用的 templates
里创建一个 base.html
文件,里面包含页头和页脚内容。
<!-- blog/templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 这是基础模版(父模版) -->
<header>这是页头</header>
<!-- 这里使用block占位符接收不同的内容 -->
<!-- 标签都是一组的,有block就有endblock -->
<!-- 这个占位符的名字叫 content -->
{% block content %}
{% endblock %}
<footer>这是页脚</footer>
</body>
</html>
上面的代码中有 {% block content %}
和 {% endblock %}
,占位符的意思,而且这个占位符的名字叫 content
。
然后在 blog.html
里引入这个父模板,把内容插入到 content
位置里。
<!-- blog/templates/blog.html -->
{% extends 'base.html' %}
<!-- 继承模版 -->
{% block content %}
<h1>雷猴雷猴</h1>
{% endblock %}
这代码第一行 {% extends 'base.html' %}
表示引入 base.html
文件,这里需要写入 base.html
文件地址,因为我把 base.html
和 blog.html
放在同一个目录下,所以可以这样直接引入。
然后使用 {% extends 'base.html' %}
表示从这里开始使用 base.html
模板,在 {% block content %}
和 {% endblock %}
之间插入自定义内容即可。
模板包含
包含的意思可以理解为前端的组建,写好的组件可以在不同地方重复调用。
比如我创建一个 com.html
文件,这个文件就是可复用组件。
<!-- blog/templates/com.html -->
<!-- 可复用组件 -->
<div>这是一个可复用组件啊啊啊啊啊</div>
然后在需要用到的地方使用它。
<!-- blog/templates/blog.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>引入可复用组件</title>
</head>
<body>
{% include 'com.html' %}
{% include 'com.html' %}
{% include 'com.html' %}
{% include 'com.html' %}
</body>
</html>
点赞 + 关注 + 收藏 = 学会了