1. 模版介绍
在Django中 , 模板 ( Templates ) 主要用于动态地生成HTML页面 .
当需要基于某些数据 ( 如用户信息 , 数据库查询结果等 ) 来动态地渲染HTML页面时 , 就会使用到模板 .
以下是模板在Django中使用的几个关键场景 :
* 1. 动态内容生成 : 当需要根据数据库中的数据或其他动态数据来生成HTML页面时 , 会使用模板 .
例如 , 显示一个包含用户信息或博客文章的列表 .
* 2. 用户交互反馈 : 在用户提交表单后 , 可能需要基于用户输入的数据来生成一个确认页面或错误消息 .
模板可以根据这些输入动态地生成这些页面 .
* 3. 网站导航和布局 : 可以使用模板来定义网站的整体布局和导航结构 .
这些布局和导航可能是静态的 , 但通常它们也会包含一些动态元素 , 如当前页面的标题或链接 .
* 4. 代码复用 : 通过使用模板继承 , 可以创建一个包含网站通用元素 ( 如页眉 , 页脚 , 侧边栏等 ) 的基础模板 .
然后 , 可以在其他模板中继承这个基础模板 , 只添加或覆盖特定部分 , 从而复用代码并提高可维护性 .
* 5. 国际化 ( i18n ) 和本地化 ( l10n ) : Django的模板系统支持国际化和本地化 ,
允许根据用户的语言或地区设置来动态地生成不同语言的页面 .
* 5. 发送电子邮件 : 虽然这不是HTML页面的直接渲染 , 但Django的模板系统也经常被用于生成动态电子邮件的内容 .
可以使用与渲染网页相同的模板来生成个性化的电子邮件消息 .
在Django中 , 模板通常与视图 ( Views ) 和模型 ( Models ) 一起工作 .
视图负责处理请求并准备要传递给模板的上下文数据 , 模型则负责处理与数据库相关的操作 .
模板则负责将这些数据渲染为HTML页面 , 这种MTV的设计模式使得Django能够高效地处理复杂的Web应用程序 .
在Django中编写视图函数时 , 可以直接在Python代码中硬编码HTML , 例 :
from django. shortcuts import HttpResponse
import datetime
def current_datetime ( request) :
now = datetime. datetime. now( )
formatted_now = now. strftime( '%Y-%m-%d %H:%M:%S' )
html = f"""
<html lang="en">
<head>
<meta charset="UTF-8">
<title>当前时间</title>
</head>
<body>
<h2> 现在的时间为: { formatted_now} . </h2>
</body>
</html>
"""
return HttpResponse( html)
尽管这种技术便于解释视图是如何工作的 , 但直接将HTML硬编码到你的视图里却并不是一个好主意 .
因为将HTML与Python代码混合在一起会导致一些问题 :
* 1. 维护困难 : 对页面设计的任何修改都需要同时修改Python代码 , 这增加了维护的复杂性 .
站点设计的修改往往比底层Python代码的修改要频繁得多 , 因此如果可以在不进行Python代码修改的情况下变更设计 , 那将会方便得多 .
* 2. 职责不明确 : HTML设计和Python编程是两项不同的技能 , 将它们混合在一起会导致职责不明确 , 可能阻碍团队中的专业分工 .
* 3. 效率低下 : 当设计者和开发者需要同时编辑一个包含HTML和Python的文件时 , 他们可能会互相等待 , 导致工作效率降低 .
为了解决这些问题 , Django引入了模板系统 , 它允许我们将HTML代码从Python视图中分离出来 , 放在独立的模板文件中 .
这样 , 就可以在不影响Python代码的情况下修改页面设计 , 同时允许不同的团队成员专注于各自擅长的领域 .
使用Django模板系统的基本步骤 :
* 1. 创建模板文件夹 : 在的Django项目的应用目录或项目根目录下创建一个名为templates的文件夹 ( 如果还没有的话 ) .
* 2. 配置模板路径 : 在项目的settings . py文件中 , 找到TEMPLATES配置项 , 确保DIRS列表中包含了存放模板的文件夹路径 .
* 3. 创建模板文件 : 在templates文件夹中创建 . html文件作为模板文件 . 这些文件将包含HTML代码以及Django模板标签和过滤器 .
* 4. 在视图中使用模板 : 在视图函数中 , 使用render ( ) 函数来加载并渲染模板 .
这个函数接受三个参数 : 请求对象 , 模板文件名和一个包含上下文数据的字典 .
* 5. 在模板中使用变量 : 在模板文件中 , 可以使用模板语法来访问传递给它的上下文变量 .
在模板引擎中 , 一旦传递了这样的上下文对象 , 模板就可以通过键 ( key ) 来访问这些值 .
模板引擎内部会解析模板字符串 , 查找所有的变量引用 , 然后将这些引用替换为上下文中相应键的值 .
TEMPLATES = [
{
'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' ,
] ,
} ,
} ,
]
在Django中 , TEMPLATES设置用于配置模板引擎 .
下面将详细解释你给出的配置中的各个部分:
* 1. BACKEND : 指定了Django应该使用哪个模板后端 .
'django.template.backends.django.DjangoTemplates' 是Django的默认模板引擎 ,
它提供了对模板标签 , 过滤器 , 加载器等功能的全面支持 .
* 2. DIRS : 这是一个列表 , 包含Django在查找模板时要搜索的目录 .
在配置中 , 使用了Python3 . 6 + 的文件路径操作 ( BASE_DIR / 'templates' ) ,
这意味着Django将在项目的根目录下的templates文件夹中查找模板 .
BASE_DIR 通常是一个指向项目根目录的变量 , 它在settings . py文件的开始部分被定义 .
* 3. APP_DIRS : 这是一个布尔值 , 如果设置为True , Django将在每个已安装的应用的templates子目录中查找模板 .
这允许你在应用的内部包含模板 , 而无需在项目的templates目录中放置它们 .
假设有一个名为myapp的应用 , 并且想在这个应用的templates目录下创建一个名为index . html的模板 .
由于APP_DIRS设置为True , Django将在 myapp / templates / index . html中查找这个模板 .
同时 , 如果在项目根目录下的templates目录中也有一个index . html文件 ,
Django将首先查找应用目录中的模板 , 然后查找项目目录中的模板 .
如果不想这样 , 可以将APP_DIRS设置为False , 但通常保留它为True是个好主意 , 因为它允许在应用的内部包含模板 .
* 4. OPTIONS : 这是一个字典 , 包含模板引擎的选项 .
context_processors : 这是一个列表 , 包含要使用的上下文处理器 .
上下文处理器允许你在每个模板的上下文中添加额外的变量 .
在的配置中 , 包含了Django的几个默认上下文处理器 :
- 'django.template.context_processors.debug' : 添加一个debug变量到上下文中 , 表示是否在调试模式下运行 .
- 'django.template.context_processors.request' : 添加一个request对象到上下文中 , 它包含了当前的HTTP请求 .
- 'django.contrib.auth.context_processors.auth' : 添加与认证系统相关的变量 , 如当前登录的用户 .
- 'django.contrib.messages.context_processors.messages' : 添加与消息框架相关的变量 , 用于显示一次性消息给用户 .
注意事项 :
* 1. 确保你的模板目录结构清晰,并且你的模板文件名是唯一的,以避免在多个地方有相同名称的模板时产生混淆。
* 2. 在生产环境中 , 可能希望禁用debug上下文处理器 , 因为它可能会暴露敏感信息 .
这可以通过从context_processors列表中移除 'django.template.context_processors.debug' 来实现 .
from django. contrib import admin
from django. urls import path, include
urlpatterns = [
path( 'admin/' , admin. site. urls) ,
path( 'index/' , include( ( 'index.urls' , 'index' ) , namespace= 'index' ) )
]
from django. urls import path
from index. views import current_datetime
urlpatterns = [
path( 'current_datetime/' , current_datetime, name= 'current_datetime' )
]
from django. shortcuts import render
import datetime
def current_datetime ( request) :
now = datetime. datetime. now( )
formatted_time = now. strftime( '%Y-%m-%d %H:%M:%S' )
context = { 'current_datetime' : formatted_time}
return render( request, 'current_datetime.html' , context)
在上面的例子中 , current_datetime . html是模板文件的名称 , 它应该位于配置的模板文件夹中 .
context字典包含了要在模板中使用的变量 .
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> 当前时间</ title>
</ head>
< body>
< h2> 现在的时间为: {{ current_datetime }}. </ h2>
</ body>
</ html>
在这个例子中 , { { current_date } } 将被替换为传递给模板的current_date变量的值 .
启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 /index/current_datetime/ .
2. 模版语法之变量
在Django模板语法中 , 有三种主要的模板组件 : 变量 ( variables ) , 过滤器 ( filters ) , 标签 ( tags ) .
2.1 变量使用方法
模板语法中的变量 ( variables ) 使用方法和语法格式 , 可以总结为以下几个方面 :
* 1. 变量名的构成 .
- 变量名必须由字母 , 数字 , 下划线 ( 不能以下划线开头 ) 和点组成 .
- 变量名的命名方式遵循常见的编程规范 .
* 2. 变量的使用语法 .
- 变量表示方法 : 在模板中 , 变量用双大括号 { { 变量名 } } 来表示 .
变量名对应于传递给模板的上下文 ( context ) 中的键值对的键 .
- 变量类型 : 变量可以是数值 , 字符串 , 布尔值 , 字典 , 对象 , 列表等基本类型 .
复杂类型 ( 如字典 , 对象 , 列表 ) 的元素或属性 , 可以通过点 ( . ) 来引用 .
- 复杂类型的引用方法 : 对于字典类型 , 使用 { { 字典名 . 键名 } } 来访问对应的值 .
对于对象类型 , 使用 { { 对象名 . 属性名 } } 或 { { 对象名 . 方法名 } } 来访问对象的属性或方法 .
( 注意 : 通常模板引擎会优先尝试访问属性 , 如果属性不存在 , 再尝试调用方法 ) .
对于列表或元组类型 , 使用 { { 列表名 . 索引 } } 来访问元素 ( 注意 : 索引从 0 开始 , 且不能使用负索引 ) .
* 3. 注意事项 :
- 变量在模板渲染时会被其值所替代 .
- 运算符 ( 如比较运算符 , 布尔运算符 ) 在模板语句中使用时 , 需要注意其左右两侧不能紧挨变量或常量 , 必须有空格 .
- 可以通过连续的句点符来访问嵌套的数据结构 .
- 使用点 ( . ) 来引用复杂类型 ( 如对象 , 字典或列表 ) 的属性或方法时 , 模板引擎会遵循一定的查找顺序来解析这个引用 .
这通常意味着 , 如果模板引擎在尝试访问一个属性时失败 ( 例如 , 该属性不存在 ) ,
它可能会尝试将该点后面的部分解释为一个方法 , 并尝试调用该方法 ( 如果该方法存在 ) .
- 可以引用对象的无参数方法 , 例 : name = 'kid' , 使用 { { name . upper } } 将字符串转换为大写 'KID' .
- 视图中不要定义包含前导空格的字典键 ( 比如 : ' message' ) , 由于模板标签无法解析包含空格的键作为有效的变量名 .
2.2 变量使用示例
以下是一个使用模板语法变量的简单示例 :
from django. urls import path
from index. views import current_datetime, template_variables
urlpatterns = [
path( 'current_datetime/' , current_datetime, name= 'current_datetime' ) ,
path( 'template_variables/' , template_variables, name= 'template_variables' ) ,
]
from django. template import Template, Context
from django. http import HttpResponse
def template_variables ( request) :
context = {
'name' : '张三' ,
'age' : 30 ,
'person' : {
'name' : '李四' ,
'age' : 25
} ,
'numbers' : [ 1 , 2 , 3 , 4 , 5 ]
}
template_str = '''
<html>
<body>
<h1>姓名: {{ name }}</h1>
<h1>年龄: {{ age }}</h1>
<h1>另一个人的姓名: {{ person.name }}</h1>
<h1>列表的第一个元素: {{ numbers.0 }}</h1>
</body>
</html>
'''
template = Template( template_str)
rendered_template = template. render( Context( context) )
return HttpResponse( rendered_template)
这里 , 使用了Django的Template和Context类来手动完成模板的渲染过程 .
代码 : template = Template ( template_str ) : 这行代码创建了一个Template对象 , 它表示一个Django模板 .
这里 , template_str是一个包含模板代码的字符串 .
在Django中 , 模板通常被保存在 . html文件中 , 但在这个例子中 , 直接从字符串中创建模板 .
代码 : rendered_template = template . render ( Context ( context ) ) 做了两件事 :
Context ( context ) : 这创建了一个Context对象 , 它存储了模板渲染时所需要的所有变量和它们的值 .
这里 , context是一个字典 , 其中包含了要在模板中使用的变量 .
传递给Context构造函数的context字典会转换为一个上下文对象 , 这样模板引擎就可以访问这些变量了 .
template . render ( . . . ) : 这是Template对象的一个方法 , 用于渲染模板 .
它接受一个Context对象作为参数 , 并返回一个字符串 , 该字符串是模板渲染后的结果 .
在渲染过程中 , 模板引擎会查找Context对象中的所有变量 , 并将它们替换为相应的值 .
最后 , rendered_template变量就包含了渲染后的模板内容 , 可以将它作为HTTP响应的一部分返回给客户端 .
注意 : 虽然这种方法在某些情况下可能很有用 ( 例如 , 当需要动态生成模板时 ) .
但在Django项目中 , 更常见的做法是将模板保存为文件 , 并使用Django的render函数来渲染它们 .
这样做的好处是模板可以被缓存 , 并且更容易在项目中组织和管理 .
启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 /index/template_variables/ , 结果如下 :
尝试访问不存在的属性 , 但存在同名方法示例 : Person类没有age属性 , 但有一个名为age的方法 :
def template_test ( request) :
name = 'kid'
class Person :
def __init__ ( self, name) :
self. name = name
def age ( self) :
return 30
qq = Person( 'qq' )
qz = Person( 'qz' )
person_list = [ qq, qz]
return render( request, 'template_test.html' , locals ( ) )
locals ( ) 函数返回的是当前局部符号表的字典 , 它包含了当前作用域内的所有局部变量 .
在模板中 , 访问age属性 : < p > { { person . age } } < / p >
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> Title</ title>
</ head>
< body>
< p> {{ person_list.0.name }}</ p>
< p> {{ person_list.1.name }}</ p>
< p> {{ person_list.0.age }}</ p>
< p> {{ person_list.1.age }}</ p>
< p> {{ name.upper }}</ p>
</ body>
</ html>
模板引擎会首先尝试查找age属性 , 但找不到 .
然后 , 由于age实际上是一个方法 , 模板引擎可能会尝试调用这个方法 ( 这取决于模板引擎的实现和配置 ) .
添加template_test视图的路由 :
from django. urls import path
from index. views import current_datetime, template_variables, template_test
urlpatterns = [
path( 'template_test/' , template_test, name= 'template_test' ) ,
]
启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 /index/template_test/ , 结果如下 .
3. 模版语法之过滤器
在Django模板系统中 , 过滤器 ( filters , 也称为模板标签或模板过滤器 ) 用于修改变量的输出格式 .
它们通常用于文本数据 , 如字符串 , 日期 , 数字等 , 以便在渲染时以特定的方式显示 .
过滤器是通过管道符号 ( | ) 应用于变量的 .
3.1 常用过滤器介绍
以下是这些过滤器的详细解释和示例 :
add : 此过滤器将变量的值加上给定的数字 .
示例 : { { my_number | add : 4 } } , 如果my_number是 3 , 则输出将是 7.
addslashes : 将字符串中的单引号 ( ' ) 和双引号 ( " ) 前添加反斜杠 ( \ ) . .
示例 : { { value | addslashes } } , 如果value的值是 "'Hello, World!'" , 则输出将是 " \'Hello, World!\'"
capfirst : 将字符串的第一个字符转换为大写 .
示例 : { { value | capfirst } } , 如果value是 "hello" , 则输出 "Hello" .
center : 将字符串居中 , 并用空格填充到指定的长度 .
示例 : { { value | center : "20" } } , 如果value是 "test" , 则输出类似 " test " ( 两边各有 7 个空格 ) .
cut : 移除变量值中所有与给定字符串相同的部分 .
示例 : { { my_string | cut : "要移除的字符串" } } ,
如果my_string是 "Hello, 要移除的字符串world!" , 则输出将是 "Hello, world!" ( 注意中间的空格仍然存在 ) .
date : 将日期格式化为指定的格式 .
示例 : { { my_date | date : "Y-m-d" } } ,
如果my_date是一个日期对象 , 并且格式设置为 "Y-m-d" , 则输出将是 "年-月-日" 格式的日期字符串 .
dictsort : 根据字典中的某个字段对字典列表进行排序 .
示例 : { { my_dict_list | dictsort : "key_name" } } , 假设字典列表my_dict_list = [
{ 'name' : '张三' , 'age' : 20 } , { 'name' : '李四' , 'age' : 25 } , { 'name' : '王五' , 'age' : 22 } ] ,
如果key_name为 "age" , 输出结果 : [ { 'name' : '张三' , 'age' : 20 } , { 'name' : '王五' , 'age' : 22 } , { 'name' : '李四' , 'age' : 25 } ]
dictsortreversed : 根据字典中的某个字段对字典列表进行反向排序 .
示例 : { { my_dict_list | dictsortreversed : "key_name" } } , 同上例
divisibleby : 判断一个数是否可以被另一个数整除 .
示例 : { % if number | divisibleby : "3" % } , 如果number是 6 , 则条件为真 .
default : 如果变量为False或为空 ( 例如空字符串 , 空列表 , None , False , 0 , 空字典等 ) , 则使用默认值 .
示例 : { { my_variable | default : "默认值" } } , 如果my_variable未定义或为空 , 则输出为 "默认值" .
default_if_none与default类似 , 但仅当变量为None时才使用默认值 .
示例 : { { my_variable | default_if_none : "默认值" } } , 如果my_variable是None , 则输出为 "默认值" .
escape : 将字符串中的HTML特殊字符转换为对应的HTML实体 , 防止XSS .
示例 : { { value | escape } } 如果value包含HTML标签 , 则会被转义 .
filesizeformat : 将字节数转换为更易读的格式 ( KB , MB , GB等 ) .
示例 : { { filesize | filesizeformat } } , 如果filesize是 1024 , 则输出 "1.0 KB" .
first : 返回列表或查询集中的第一个元素 ( 通常在模板标签外部使用 ) .
示例 : { { my_list | first } } , 对于列表 [ 1 , 2 , 3 , 4 , 5 ] , 输出 1.
floatformat : 格式化浮点数 , 可以指定小数点后的位数 .
示例 : { { number | floatformat : "2" } } 如果number是 3.14159 , 则输出 "3.14" .
join : 此过滤器使用指定的分隔符将列表中的元素连接成一个字符串 .
示例 : { { my_list | join : ", " } } , 如果my_list是 [ 'apple' , 'banana' , 'cherry' ] , 则输出将是 "apple, banana, cherry" .
length : 返回变量的长度 . 对于字符串 , 它返回字符数 ; 对于列表 , 元组或字典等 , 它返回项目的数量 .
示例 : { { my_list | length } } , 如果my_list是 [ 'apple' , 'banana' , 'cherry' ] , 则输出为 3.
linebreaks : 用 < p > 和 < / p > 标签包裹变量中的换行符 .
示例 : { { value | linebreaks } } , 如果value包含换行符 , 则会被 < p > 标签包裹 .
linebreaksbr : 用 < br / > 标签替换变量中的换行符 .
示例 : { { value | linebreaksbr } } , 如果value包含换行符 , 则会被 < br / > 替换 .
linenumbers : 为变量中的每一行加上行号 .
示例 : { { value | linenumbers } } , 如果value的值为 'xxx' , 第一行显示 : 1. xxxx .
ljust : 将字符串左对齐到指定长度 , 并使用空格填充 .
示例 : { { value | ljust : "10" } } , 如果value是 "test" , 则输出类似 "test " ( 右边有 6 个空格 ) .
pluralize : 将单词的单数形式转换为复数形式 ( 如果单词后跟的数字不是 1 ) .
示例 : { { count } } { { item | pluralize : "apple,apples" } } , 如果count是 2 , item是 "apple" , 则输出 "2 apples" .
removetags : 从字符串中删除指定的HTML标记 .
示例 : { { value | removetags : "b i" } } , 如果value包含 < b > 和 < i > 标签 , 则这些标签会被删除 .
rjust : 将字符串右对齐到指定长度 , 并使用空格填充 .
示例 : { { value | rjust : "10" } } , 如果value是 "test" , 则输出类似 " test" ( 左边有 6 个空格 ) .
slice : 切片操作 , 返回列表或字符串的一个切片 .
示例 : { { some_list | slice : "2:5" } } , 对于列表 [ 1 , 2 , 3 , 4 , 5 ] , 输出 [ 3 , 4 , 5 ] .
示例 : { { some_string | slice : "2:5" } } , 对于字符串 "hello" , 输出 "llo" .
slugify : 将字符串转换为全小写 , 并将空格替换为连字符 ( "-" ) , 并删除特殊字符 .
示例 : { { value | slugify } } 如果 value 是 "Hello, world!" ,则输出将是 "hello-world" 。
stringformat : 使用Python的字符串格式化语法来格式化字符串 .
示例 : { { value | stringformat : "s" } } ,
这里 "s" 是Python的字符串格式说明符 , 但它通常与更复杂的格式化一起使用 , 例如 "%.2f" 用于浮点数 .
safe : 此过滤器标记一个字符串是安全的 , 即不需要进行HTML转义 . 这在想要直接渲染HTML标签时非常有用 .
示例 : { { my_html_string | safe } } ,
如果my_html_string包含HTML标签 , 如 < b > Bold text < / b > , 则safe过滤器将确保这些标签在模板中按原样渲染 , 而不是被转义为文本 .
当使用safe过滤器时 , 必须确保传递给它的字符串是安全的 , 因为它会绕过Django的自动转义机制 ,
这可能会使您的网站容易受到跨站脚本攻击 ( XSS ) .
在可能的情况下 , 最好避免使用safe过滤器 , 或者使用其他方法来确保字符串的安全性 .
time : 将时间格式化为指定的格式 .
示例 : { { my_time | time : "H:i:s" } } ,
如果my_time是一个时间对象 , 并且格式设置为 "H:i:s" , 则输出将是 "时:分:秒" 格式的时间字符串 .
timesince : 将日期格式化为自该日期以来的时间 ( 例如 : '4 days, 6 hours' ) .
示例 : { { my_date | timesince } } , 如果my_date是一个过去的日期时间对象 , 则输出将是自那时起经过的时间 .
timeuntil : 计算从现在到给定日期或时间的时间 ( 例如 : '4 days, 6 hours' ) .
示例 : { { my_date | timeuntil } } , 如果my_date是一个未来的日期时间对象 , 则输出将是从现在到那时的时间间隔 .
truncatewords : 如果字符串字符数多于指定的字符数量 , 则截断该字符串 , 并在末尾添加可翻译的省略号序列 ( . . . ) .
示例 : { { value | truncatewords : "5" } }
如果value是 "Hello, how are you today?" , 则输出 "Hello, how are..." .
truncatewords_html : 用于截断HTML字符串中的单词数量 , 同时尝试保持HTML标签的完整性 .
示例 : { { value | truncatewords_html : "5" } } ,
如果value的值是 : < p > 这是一个 < b > 很长的 < / b > HTML字符串 , 它包含了很多 < i > 信息 < / i > < / p > ,
则输出 : < p > 这是一个 < b > 很长的 < / b > HTML . . . < / p > .
title : 此过滤器将字符串转换为标题格式 , 即每个单词的首字母大写 .
示例 : { { my_string | title } } , 如果my_string是 "hello world" , 则输出将是 "Hello World" .
lower : 此过滤器将字符串转换为小写 .
示例 : { { my_string | lower } } , 如果my_string是 "HELLO WORLD" , 则输出将是 "hello world" .
length_is : 此过滤器用于在模板标签 ( 如 : if标签 ) 中检查变量的长度是否等于给定的值 .
示例 : { % if my_list | length_is : "5" % } 列表有 5 个项目 { % endif % } , 如果my_list的长度是 5 , 则输出 '列表有5个项目' .
upper : 此过滤器将字符串转换为大写 .
示例 : { { my_string | upper } } , 如果my_string是 "hello world" , 则输出将是 "HELLO WORLD" .
urlencode : 对字符串中的特殊字符进行URL编码 .
示例 : { { value | urlencode } } 如果value是 "Hello, world! & you?" , 则输出 : "Hello%2C+world%21+%26+you%3F" .
urlize : 将文本中的URL转换为可点击的链接 .
示例 : { { value | urlize } } , 如果value是 "Visit https://www.example.com" , 则输出将是带有 < a > 标签的链接 .
wordcount : 计算字符串中的单词数。
示例 : { { value | wordcount } } , 如果 value 是 "apple banana cherry" ,则输出是 3 。
yesno : 根据给定的值返回 'yes' , 'no' 或 'maybe' .
示例 : { { value | yesno : "yes,no,maybe" } } ,
如果value是True , 则输出是 "yes" ; 如果value是False , 则输出是 "no" ; 如果value是None或空 , 则输出是 "maybe" ( 或提供的第三个参数 ) .
请注意 , yesno过滤器通常接受三个参数 , 分别对应真 , 假和未定义的情况 . 如果只提供一个参数 , 则默认值是 : "yes" , "no" 和 "maybe" .
Django提供了许多其他过滤器 , 可以根据需要在Django文档中查找它们 .
地址 : https : / / docs . djangoproject . com / zh-hans / 3.2 /ref/templates/builtins/ .
在模板中使用过滤器时 , 请确保了解它们的工作原理和潜在的安全风险 .
3.2 过滤器使用示例
下面例子中views . py文件为模板提供了一个字典和几个字符串 , 数字 , 列表和日期对象 , 然后在模板中使用Django过滤器来格式化这些数据 .
from django. urls import path
from index. views import filter_examples
urlpatterns = [
path( 'filter_examples/' , filter_examples, name= 'filter_examples' ) ,
]
from django. shortcuts import render
from datetime import datetime
def filter_examples ( request) :
name = "Alice"
price = 123.456
description = "This is a really long description that needs to be truncated."
is_published = True
items = [ "apple" , "banana" , "cherry" ]
date_now = datetime. now( )
context = {
'name' : name,
'price' : price,
'description' : description,
'is_published' : is_published,
'items' : items,
'date_now' : date_now,
}
return render( request, 'filter_examples.html' , context)
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> Django Filter Examples</ title>
</ head>
< body>
< h1> 字符串过滤器</ h1>
< p> 原始字符串: {{ description }}</ p>
< p> 首字母大写: {{ name|capfirst }}</ p>
< p> 截断字符串: {{ description|truncatewords:5 }}</ p>
< p> 转换为小写: {{ name|lower }}</ p>
< p> 转换为大写: {{ name|upper }}</ p>
< p> 转换为标题格式: {{ name|title }}</ p>
< h1> 数字过滤器</ h1>
< p> 原始价格: {{ price }}</ p>
< p> 保留两位小数: {{ price|floatformat:2 }}</ p>
< h1> 列表过滤器</ h1>
< p> 列表元素数量: {{ items|length }}</ p>
< p> 列表第一个元素: {{ items.0 }}</ p>
< p> 列表被连接成的字符串: {{ items|join:", " }}</ p>
< h1> 布尔过滤器</ h1>
< p> 是否发布: {{ is_published|yesno:"已发布,未发布" }}</ p>
< h1> 日期和时间过滤器</ h1>
< p> 原始日期: {{ date_now }}</ p>
< p> 日期格式: {{ date_now|date:"Y-m-d" }}</ p>
< p> 时间格式: {{ date_now|time:"H:i:s" }}</ p>
< p> 自定义日期时间格式: {{ date_now|date:"l, F j, Y, P e" }}</ p>
</ body>
</ html>
启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 /index/filter_examples/ , 结果如下 :
3.3 HTML标签转义
默认情况下 , Django会对所有的变量进行HTML转义 , 以防止跨站脚本攻击 ( XSS ) .
跨站脚本攻击 ( Cross-Site Scripting , 简称 XSS ) 是一种安全漏洞 , 它允许攻击者在受害者的浏览器上执行恶意脚本 ( 后续说 ) .
这意味着 , 如果有一个变量包含HTML标签 , Django会将这些标签转换为它们的HTML实体形式 , 从而防止它们被浏览器解析为真实的HTML .
def my_view ( request) :
my_html = "<p>这是一段HTML内容. </p>"
return render( request, 'my_template.html' , { 'safe_html' : my_html} )
在HTML中 , < 和 > 是特殊字符 , 它们被用作标签的开始和结束 .
如果希望在HTML文档中显示这些字符本身而不是作为标签的一部分 , 需要使用它们的字符实体 ( character entity ) 来表示它们 .
对于 < , 字符实体是 & lt ;
对于 > , 字符实体是 & gt ;
浏览器在渲染这段HTML时 , 会显示文本内容而不是解析 < p > 作为HTML标签 .
如果确实需要在模板中渲染HTML内容 , 并且确信内容是安全的 , 可以使用几种方法来绕过转义机制 .
Django模板引擎提供了几种方式来渲染HTML内容 :
* 1. 在视图中使用mark_safe : 在视图中使用mark_safe函数来标记一个字符串为安全的 , 并将其传递给模板 .
导入模块语句 : from django . utils . safestring import mark_safe .
* 2. 在模板中使用 | safe过滤器 : 在Django模板中 , 可以使用 | 管道符来应用过滤器 ,
safe过滤器与mark_safe函数在功能上相似 , 它告诉模板引擎不要对变量进行转义 .
from django. urls import path
from index. views import my_view
urlpatterns = [
path( 'my_view/' , my_view, name= 'my_view' ) ,
]
from django. shortcuts import render
from django. utils. safestring import mark_safe
def my_view ( request) :
my_html = "<p>这是一段HTML内容。</p>"
safe_html = mark_safe( my_html)
context = {
'my_html' : my_html,
'safe_html' : safe_html
}
return render( request, 'my_template.html' , context)
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> safe_test</ title>
</ head>
< body>
{{ my_html }}
{{ my_html|safe }}
{{ safe_html }}
</ body>
</ html>
启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 /index/my_view/ , 结果如下 :
4. 模版语法之标签
在Django模板中 , 标签 ( tags ) 是用于执行逻辑或循环结构的指令 , 它们确实通常被包裹在 { % 和 % } 之间 .
Django模板标签提供了很多功能 , 包括但不限于控制流 , 循环 , 加载静态文件 , 包含其他模板等 .
使用需要明确开始和结束标签的模板标签时 , 就需要闭合标签 . 这些标签通常用于控制流 ( 如条件判断和循环 ) 或包含其他模板内容 .
4.1 常用标签介绍
下面是一些Django模板中常用的标签示例 :
* 1. for标签 : 用于在模板中遍历列表 , 元组 , 字典等可迭代对象 .
< ul>
{% for item in items %}
< li> {{ item }}</ li>
{% endfor %}
</ ul>
{% for item in items %}
< p> {{ item }}</ p>
{% endfor %}
{% for key,val in dic.items %}
< p> {{ key }}:{{ val }}</ p>
{% endfor %}
{% for item in items %}
< p> {{ items }}</ p>
{% empty %}
< p> 容器为空!</ p>
{% endfor %}
* 2. if标签 : 用于在模板中进行条件判断 .
if语句支持and , or , = = , > , < , ! = , < = , > = , in , not in , is , is not判断 .
{% if is_published %}
< p> This item is published.</ p>
{% else %}
< p> This item is not published.</ p>
{% endif %}
如果is_published是True , 则显示 'This item is published.' , 否则显示 'This item is not published.' .
* 3. ifequal / ifnotequal 标签 : 这两个标签用于比较两个值是否相等或不相等 .
{% ifequal section "news" %}
< h1> News Section</ h1>
{% endifequal %}
{% ifnotequal section "news" %}
< h1> Not the News Section</ h1>
{% endifnotequal %}
注意 : ifequal 和 ifnotequal 已经不再推荐使用 , 建议使用if标签和比较运算符 ( = = 或 ! = ) 替代 .
* 4. with标签 : 用于在模板中存储一个变量值到一个新的变量名中 , 以便在模板的其余部分中使用 .
with标签允许你为复杂的变量或 '昂贵的' 操作 ( 如数据库查询 ) 的结果设置一个简单的别名 .
这在需要在模板中多次引用这个变量或结果时非常有用 , 因为它可以避免重复执行相同的操作 , 从而提高性能 .
举个例子 , 假设有一个模板变量user_profile , 它是通过调用一个数据库查询或者其他复杂操作得到的 .
如果你在模板中多次使用user_profile , 那么每次使用它时都会重新执行那个复杂的操作 , 这显然是低效的 .
使用with标签 , 可以为user_profile分配一个别名 , 比如profile , 然后在with标签的块内部多次使用profile而不是user_profile .
这样做的好处是 , Django只会在进入with块时执行一次user_profile的操作 , 并将结果存储在profile中 ,
然后在块内部多次使用profile时 , 它不会再次执行那个操作 .
{% with total=business.employees.count %}
< p> {{ total }} employee{{ total|pluralize }}</ p>
{% endwith %}
* 5. url标签 : 用于在模板中生成URL , 它通常与Django的URL配置结合使用 .
示例 : < a href = "{% url 'detail' item.id %}" > View { { item . name } } < / a > .
在上面的例子中 , detail是URL配置中的一个命名URL模式 , item . id是传递给这个URL模式的参数 .
* 6. include标签 : 用于在模板中包含另一个模板的内容 .
示例 : { % include 'header.html' % } , HTML代码将会包含名为header . html的模板的内容 .
* 7. csrf_token标签 : 在表单中 , csrf_token标签用于包含跨站请求伪造 ( CSRF ) 保护令牌 .
< form method = " post" >
{% csrf_token %}
</ form>
* 8. load标签 : 用于加载自定义的模板标签库 .
示例 : { % load my_custom_tags % } , 加载了名为my_custom_tags的自定义标签库后 , 就可以在模板中使用该库中的标签了 .
这只是Django模板标签中的一小部分示例 , Django还提供了许多其他的标签 , 可以满足更复杂的模板需求 .
要获取完整的标签列表和文档 , 请参考Django的官方文档 .
文档地址 : https : / / docs . djangoproject . com / zh-hans / 3.2 /ref/templates/builtins/ .
4.2 for标签使用示例
为了演示上述模板标签的使用 , 创建一个Django视图 , 并传递一个包含人员列表的上下文到模板中 .
from django. shortcuts import render
def person_list_view ( request) :
person_list1 = [
{ 'name' : 'Alice' } ,
{ 'name' : 'Bob' } ,
{ 'name' : 'Charlie' } ,
]
person_list2 = [ ]
context = {
'person_list1' : person_list1,
'person_list2,' : person_list2
}
return render( request, 'person_list.html' , context)
然后 , 在模板文件person_list . html中 , 可以使用for标签来遍历人员列表 , 并显示每个人的名字 .
同时 , 使用 { % empty % } 来处理列表为空的情况 :
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> Person List</ title>
</ head>
< body>
< h1> Persons</ h1>
< ul>
{% for person in person_list1 %}
< li> {{ person.name }}</ li>
< p> Iteration: {{ forloop.counter }}</ p>
{% empty %}
< li> Sorry, no person here</ li>
{% endfor %}
</ ul>
< br>
< ul>
{% for person in person_list2 %}
< li> {{ person.name }}</ li>
< p> Iteration: {{ forloop.counter }}</ p>
{% empty %}
< li> Sorry, no person here</ li>
{% endfor %}
</ ul>
</ body>
</ html>
最后 , 在urls . py文件中添加一个URL模式来映射视图 :
from django. urls import path
from index. views import person_list
urlpatterns = [
path( 'person_list/' , person_list, name= 'person_list' ) ,
]
启动项目 , 访问 : 127.0 .0 .1 : 8000 /index/person_list/ .
模板中如果person_list列表不为空 , 它将显示每个人的名字和循环的序号 ; 如果列表为空 , 它将显示 'Sorry, no person here' .
4.3 forloop特殊变量
在Django模板语言中 , forloop是一个在for循环内部可用的特殊变量 , 它提供了关于当前循环迭代的信息 .
当在模板中执行一个for循环时 , Django会自动创建一个forloop变量 , 可以使用它来访问有关循环的元数据 .
forloop : 对象包含了一些属性 , 比如 :
* 1. forloop . counter : 当前循环的迭代次数 ( 从 1 开始计数 ) .
示例 : 在一个包含 5 个元素的列表中 , 第一次迭代时forloop . counter的值为 1 , 第二次为 2 , 依此类推 .
* 2. forloop . counter0 : 当前循环的迭代次数 ( 从 0 开始计数 ) .
示例 : 同样在一个包含 5 个元素的列表中 , 第一次迭代时forloop . counter0的值为 0 , 第二次为 1 , 依此类推 .
* 3. forloop . revcounter : 从循环末尾开始计算的迭代次数 ( 从 1 开始计数 ) .
示例 : 在一个包含 5 个元素的列表中 , 当迭代到最后一个元素时 , forloop . revcounter 的值为 1 ;
在倒数第二个元素时 , 其值为 2 , 依此类推 .
* 4. forloop . revcounter0 : 从循环末尾开始计算的迭代次数 ( 从 0 开始计数 ) .
示例 : 在一个包含 5 个元素的列表中 , 当迭代到最后一个元素时 , forloop . revcounter0的值为 0 ;
在倒数第二个元素时 , 其值为 1 , 依此类推 .
* 5. forloop . first : 如果这是循环的第一次迭代 , 则为Truel 否则为False .
用途 : 常用于在循环的开头添加特殊的样式或内容 .
* 6. forloop . last : 如果这是循环的最后一次迭代 , 则为Truel 否则为False .
用途 : 常用于在循环的末尾添加特殊的样式或内容 .
* 7. forloop . parentloop : 如果当前循环是嵌套循环中的内层循环 , 则forloop . parentloop引用的是外层循环的forloop对象 .
用途 : 在嵌套循环中 , 有时需要引用外层循环的信息或变量l 这时forloop . parentloop就派上了用场 .
这些属性和方法可以用于在模板中根据循环的不同阶段或迭代次数来定制输
例如 , 可以使用forloop . counter来在列表的每一项前添加序号 ,
或者使用forloop . first和forloop . last来为列表的第一项或最后一项添加特殊的样式 .
为了更清晰地说明这些forloop模板变量的用途 , 将提供一个模板示例 , 该示例同时使用了这些变量 , 并展示了它们如何在循环中工作 .
假设我们有一个包含五个元素的列表 , 并想在一个HTML表格中显示它们 , 同时添加一些额外的信息 ,
如当前循环的索引和是否是第一次或最后一次迭代 .
首先 , 确保视图返回了一个包含列表的上下文 .
from django. shortcuts import render
def forloop_example ( request) :
items = [ 'aa' , 'bb' , 'cc' , 'dd' , 'ee' ]
context = {
'items' : items,
}
return render( request, 'forloop_example.html' , context)
在模板中 , 使用for循环来遍历列表 , 并使用forloop变量来显示额外信息 .
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> For Loop Example</ title>
</ head>
< body>
< table border = " 1" >
< thead>
< tr>
< th> 索引 (从1开始)</ th>
< th> 索引 (从0开始)</ th>
< th> 倒序索引 (从1开始)</ th>
< th> 倒序索引 (从0开始)</ th>
< th> Item</ th>
< th> 是否为第一次循环?</ th>
< th> 是否为最后一次循环?</ th>
</ tr>
</ thead>
< tbody>
{% for item in items %}
< tr>
< td> {{ forloop.counter }}</ td>
< td> {{ forloop.counter0 }}</ td>
< td> {{ forloop.revcounter }}</ td>
< td> {{ forloop.revcounter0 }}</ td>
< td> {{ item }}</ td>
< td> {{ forloop.first|yesno:"True,False" }}</ td>
< td> {{ forloop.last|yesno:"True,False" }}</ td>
</ tr>
{% endfor %}
</ tbody>
</ table>
</ body>
</ html>
在这个模板中 , forloop . counter和forloop . counter0分别显示了基于 1 和基于 0 的当前迭代索引 .
forloop . revcounter和forloop . revcounter0则显示了从循环末尾开始的迭代索引 .
forloop . first和forloop . last则用于判断当前迭代是否是第一次或最后一次 .
使用yesno模板过滤器 , 将forloop . first和forloop . last的布尔值转换为更友好的 "True" 或 "False" 字符串 .
添加路由 , 确保URL配置映射到这个视图 .
from django. urls import path
from index. views import forloop_example
urlpatterns = [
path( 'example/' , forloop_example, name= 'forloop_example' ) ,
]
启动项项目 : 访问 : 127.0 .0 .1 : 8000 /index/forloop_example ,
然后会看到一个表格 , 其中列出了每个项目的详细信息 , 包括循环的各种索引和是否是第一次或最后一次迭代 .
5. 自定义模板标签
在Django中 , 自定义模板标签 ( 也称为模板过滤器或模板标签库 ) 可以扩展Django模板系统的功能 .
自定义模板标签通常包括简单函数 , 过滤器和复杂标签 , 这些都可以在Django模板中使用 .
5.1 创建模板标签
以下是创建一个自定义模板标签库的基本步骤 :
* 1. 在应用目录下创建templatetags包存放自定义模板标签 .
* 2. 编写自定义模板标签 : 在Python包中 , 创建一个Python模块 ( 例如 : my_tags . py ) , 并在其中编写自定义模板标签 .
* 3. 注册模板标签 : 创建一个名为__init__ . py的文件在你的templatetags目录下 , 并在其中导入自定义模板标签 .
这样做是为了让Django知道模板标签库的存在 .
* 4. 在模板中使用标签 : 在Django模板中 , 使用 { % load % } 标签来加载自定义模板标签库 ( 加载的文件名称不包括 . py扩展名 ) ,
然后使用 { % my_tag % } 或 { { my_filter | variable } } 来使用它们 .
注意事项 :
在Django的模板系统中 , 自定义过滤器 ( filter ) 最多只能传递一个模板变量和一个额外的参数 .
这是因为过滤器是设计为对单个变量进行操作的 , 额外的参数 ( 如果有的话 ) 通常用于修改或控制这个操作的行为 .
这个额外的参数是通过过滤器管道 ( | ) 后面的冒号 ( : ) 和值来提供的 .
使用方式 : { { value1 | add_value : value2 } } ,
add_value过滤器接受value1作为值参数 , 并通过冒号和value1变量传递了一个额外的参数 .
下面是一个简单的例子来说明如何创建一个自定义模板标签 :
* 步骤 1 : 在app目录下创建目templatetags包 .
* 步骤 2 : 编写自定义模板标签 ( my_tags . py ) .
from django import template
register = template. Library( )
@register. simple_tag
def hello_world ( ) :
return "Hello, World!"
@register. filter
def add_value ( value1, value2) :
return value1 + value2
@ register . simple_tag装饰器 : 用于创建自定义的模板标签 .
这些标签可以像函数一样在模板中被调用 , 并且可以接收任意数量的参数 .
这些参数可以是模板变量 , 硬编码的字符串或其他任何在模板中可用的东西 .
@ register . filter装饰器 : 用于创建自定义的模板过滤器 .
过滤器被设计用来处理单个值 , 并返回处理后的值 ; 它们通常用于修改变量或字符串的输出格式 .
* 3. 步骤 3 : 注册模板标签 ( __init__ . py ) .
在templatetags目录下的__init__ . py文件中 , 通常不需要添加任何内容 ,
因为Django会自动查找my_tags . py等模块中的注册标签 .
如果需要 , 可以在__init__ . py中执行一些初始化代码 , 不过通常是空的 , 不需要任何代码 . .
注意 : 在Django1 . 9 及以后的版本中 , templatetags目录和其中的__init__ . py文件是必需的 ,
以便Django能够正确找到你的自定义模板标签 .
此外 , 自定义模板标签库的名字 ( 在这个例子中是my_tags ) 应该在模板中加载它的名字相匹配 .
* 步骤 4 : 在模板中使用标签 .
注意 : 在模板文件中 , 首先加载自定义标签库 { % load my_tags % } . 然后 , 再使用自定义标签 .
from django. urls import path
from index. views import my_tags
urlpatterns = [
path( 'my_tags/' , my_tags, name= 'my_tags' ) ,
]
from django. shortcuts import render
def my_tags ( request) :
return render( request, 'index.html' )
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> 自定义标签</ title>
</ head>
< body>
{% load my_tags %}
{% hello_world %}
{{ 1 | add_value:2 }}
</ body>
</ html>
启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 /index/my_tags/ , 结果如下 :
5.2 多参数处理
在Django的模板系统中 , 标准的过滤器通常只接受一个模板变量和一个可选的参数 .
直接在一个过滤器中传递三个参数 ( 即两个额外的参数 ) 是不支持的 .
如果需要处理多个参数 , 并且这些参数之间的关系比较复杂 , 那么使用自定义模板标签可能是一个更好的选择 .
自定义模板标签可以接受任意数量的参数 , 并在模板中生成输出 .
使用上面的示例 , 修改部分文件的代码用于新的测试 :
from django import template
register = template. Library( )
@register. simple_tag ( takes_context= True )
def custom_tag ( context, arg1, arg2, arg3) :
return context[ 'num' ] + arg1 + arg2 + arg3
from django. shortcuts import render
def my_tags ( request) :
context = {
'num' : 1
}
return render( request, 'index.html' , context)
在模板中 , 可以这样使用 :
{ % load my_tags % }
{ % custom_tag arg1 arg2 arg3 % }
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> 自定义标签</ title>
</ head>
< body>
{% load my_tags %}
{% custom_tag 2 3 4 %}
</ body>
</ html>
启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 /index/my_tags/ , 结果如下 ( 1 + 2 + 3 + 4 = 10 ) :
5.3 创建可重用模板片段
inclusion_tag是Django模板标签库中的一个装饰器 , 用于创建可重用的模板片段 .
这些片段可以接收参数 , 并根据这些参数动态生成HTML内容 .
在templatetags / my_inclusion . py中定义inclusion_tag :
from django import template
register = template. Library( )
@register. inclusion_tag ( 'result.html' )
def show_results ( n) :
n = 1 if n < 1 else int ( n)
data = [ "第{}项" . format ( i) for i in range ( 1 , n+ 1 ) ]
return { "data" : data}
创建templates / result . html模板 :
< ul>
{% for choice in data %}
< li> {{ choice }}</ li>
{% endfor %}
</ ul>
在templates / index . html模板中使用show_results .
注意 , 需要先在模板的顶部加载自定义的标签库 !
{% load my_inclusion %}
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> 包含标签</ title>
</ head>
< body>
{% show_results 10 %}
</ body>
</ html>
编写路由与视图函数 , 当用户访问时返回index . html页面 .
from django. urls import path
from index. views import my_view
urlpatterns = [
path( 'my_view/' , my_view, name= 'my_view' ) ,
]
from django. shortcuts import render
def my_view ( request) :
return render( request, 'index.html' )
当渲染index . html时 , Django会调用show_results函数 , 并传入参数 10.
这个函数会生成一个包含 10 个列表项的HTML列表 , 并将其插入到index . html中 { % show_results 10 % } 的位置 .
启动项目 : 访问 : http : / / 127.0 .0 .1 : 8000 /index/my_view/ , 结果如下 :
6. 模版复用
在Django中 , 模板可以被组织在多个文件中 , 以便更好地管理和复用代码 .
为了在一个模板中复用另一个模板的内容 , Django提供了模板继承 ( template inheritance ) 和模板包含 ( template inclusion ) 的功能 .
6.1 模板继承
模板继承 ( Template Inheritance ) : 定义一个基础模板 ( 通常称为 '母版' 或 '父模板' ) ,
其中包含你的网站中所有页面通用的元素 , 如头部 , 底部 , 导航栏等 .
然后 , 可以创建其他模板来继承这个基础模板 , 并添加或覆盖特定的部分 .
注意事项 :
在Django的模板系统中 , { % extends % } 标签必须是模板中的第一个标签 ( 除了可能的注释和模板空白控制标签 ,
如 { % load % } 和 { % templatetag openblock % } / { % templatetag closeblock % } 用于自定义模板标签的块 ) .
当使用 { % extends % } 标签时 , 正在告诉Django希望当前模板继承自另一个 ( 父 ) 模板 .
父模板中的所有内容 ( 除了 { % block % } 标签定义的部分 ) 都会被加载到子模板中 ,
而子模板则可以覆盖或添加内容到这些 { % block % } 标签定义的部分 .
如果 { % extends % } 不是模板中的第一个标签 , Django将无法正确解析模板的继承关系 ,
因为它会首先尝试渲染它遇到的第一个标签之前的任何内容 , 而这在继承的上下文中可能并不适用 .
在父模板中 , 可以使用 { % block % } 和 { % endblock % } 来定义块 . 这些块是占位符 , 用于在子模板中提供具体的内容 .
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> {% block title %}My Website{% endblock %}</ title>
</ head>
< body>
< header>
< p> 这里是网站的头部 </ p>
</ header>
< main>
{% block content %}{% endblock %}
</ main>
< footer>
< p> 这里是网站的底部 </ p>
</ footer>
</ body>
</ html>
子模板使用 { % extends % } 标签来声明它继承自哪个父模板 .
然后使用相同的 { % block % } 标签和相同的名字来覆盖这个块 , 重新定义父模板中的块来提供具体的内容 .
{% extends "base.html" %}
{% block title %}Home Page{% endblock %}
{% block content %}
< p> Welcome to the Home Page!</ p>
< p> This is the content of the home page.</ p>
{% endblock %}
在上面的示例中 , home . html继承了base . html , 并覆盖了title和content块 .
编写路由和视图用于测试模板继承 :
from django. urls import path
from index. views import my_view
urlpatterns = [
path( 'my_view/' , my_view, name= 'my_view' ) ,
]
from django. shortcuts import render
def my_view ( request) :
return render( request, 'home.html' , )
启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 /index/my_view/ , 结果如下 , 继承模板并替换网站标题和网站内容 :
在Django的模板系统中 , { % block . super % } 是一个特殊的模板标签 ,
它允许子模板在覆盖或扩展父模板中的块时 , 保留父模板中该块的原始内容 .
这个标签在模板继承中特别有用 , 当想在父模板的默认内容之上或之后添加一些额外的内容时 .
当使用 { % block % } 和 { % endblock % } 在父模板中定义一个块时 , 可以提供一个默认的内容 .
在子模板中 , 可以使用相同的 { % block % } 标签和相同的名字来覆盖这个块 .
然而 , 如果还想保留父模板中该块的原始内容 , 可以使用 { % block . super % } .
沿用上面的示例修改部分代码 , 列举一个简单的例子来解释 { % block . super % } 的用法 :
from django. shortcuts import render
def my_view ( request) :
return render( request, 'child.html' , )
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> {% block title %}Default Title{% endblock %}</ title>
</ head>
< body>
< header>
< p> 这里是网站的头部 </ p>
</ header>
< main>
{% block content %}
< p> This is the default content in the parent template.</ p>
{% endblock %}
</ main>
< footer>
< p> 这里是网站的底部 </ p>
</ footer>
</ body>
</ html>
{% extends "parent.html" %}
{% block title %}My Child Page Title{% endblock %}
{% block content %}
{{ block.super }}
< p> This is additional content in the child template.</ p>
{{ block.super }}
{% endblock %}
在这个例子中 , 子模板child . html继承了父模板base . html .
它覆盖了title块和content块 .
在content块中 , 它使用了 { { block . super } } 来保留父模板中content块的原始内容 , 并在其后添加了一段新的内容 .
启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 /index/my_view/ ,
可以看到 { { block . super } } 允许子模板在覆盖块时保留父模板中的原始内容 .
6.2 模板包含
在Django的模板系统中 , 模板包含 ( Template Inclusion ) 是一种将一个模板的内容直接插入到另一个模板中的机制 .
这通常通过使用 { % include % } 标签来实现 . 这个标签接受一个模板的名称 ( 通常是一个相对路径 , 从模板加载器配置的目录开始 ) 作为参数 .
{ % include % } 标签允许在一个模板中插入另一个模板的内容 , 这对于重用模板片段 ( 如页眉 , 页脚 , 侧边栏等 ) 非常有用 .
注意事项 :
* 1. 包含的模板路径是相对于模板加载器配置的目录的 . 如果使用的是APP_DIRS选项 , 并且模板位于应用的templates目录下 ,
那么应该使用应用的名称和模板的相对路径来引用模板 ( 例如 : { % include "myapp/sidebar.html" % } ) .
* 2. 包含的模板可以包含自己的 { % include % } 标签 , 从而允许模板的嵌套包含 .
* 3. 包含的模板会继承父模板的上下文 , 也可以使用 with 选项来传递额外的变量 .
* 4. { % include % } 标签不会触发模板的继承机制 . 它只是简单地将一个模板的内容插入到另一个模板中 .
* 5. 如果需要更复杂的模板重用和扩展 , 你应该使用模板继承 .
例如 , 假设有一个名为sidebar . html的模板 , 它包含了一个侧边栏的内容 .
可以在其他模板中使用 { % include % } 标签来包含这个侧边栏 :
< div class = " sidebar" >
< h2> {{ sidebar_title }}</ h2>
{{ message }}
< ul>
< li> < a href = " /link1/" > 链接 1</ a> </ li>
< li> < a href = " /link2/" > 链接 2</ a> </ li>
< li> < a href = " /link3/" > 链接 3</ a> </ li>
</ ul>
</ div>
在这个示例中 , sidebar . html包含了一个带有标题的侧边栏 ,
标题通过 { { sidebar_title } } 变量来动态设置侧边栏内部有一个无序列表 , 列出了几个链接 ,
可以根据需要添加更多的链接或其他内容 .
在父模板 ( 如 base . html ) 中 , 可以使用 { % include "sidebar.html" with sidebar_title = "我的侧边栏" % } 来包含这个侧边栏模板 ,
并传递一个 sidebar_title 变量来设置侧边栏的标题 .
这样 , 每次渲染父模板时 , 侧边栏的内容都会根据传递的变量动态生成 .
传递变量到包含的模板 : 可以使用with选项向包含的模板传递额外的上下文变量 .
这些变量只在被包含的模板中可用 .
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> My Page</ title>
</ head>
< body>
< div id = " content" >
</ div>
< div id = " sidebar" >
{% include "sidebar.html" with sidebar_title="My Sidebar" %}
</ div>
</ body>
</ html>
在这个例子中 , base . html是主模板 , 它使用 { % include "sidebar.html" % } 来包含sidebar . html模板的内容 .
当Django渲染base . html时 , 它会找到并渲染sidebar . html模板 , 然后将结果插入到base . html中的相应位置 .
修改上面的示例的视图函数 , 返回base . html页面 .
from django. shortcuts import render
def my_view ( request) :
context = {
'message' : 'My Django' ,
}
return render( request, 'base.html' , context)
启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 /index/my_view/ , 结果如下 ,
7. 静态文件引用
7.1 静态文件的配置
在Django项目中 , 静态文件的配置主要涉及几个关键设置 , 以确保在开发环境和生产环境中都能正确地访问静态文件 .
以下是如何配置Django静态文件的步骤 :
* 1. 设置STATIC_URL .
在settings . py文件中 , 需要设置STATIC_URL , 这是静态文件在Web应用中访问的基础URL .
默认设置 : STATIC_URL = '/static/' , 表示静态文件的基础URL路径 .
这个设置告诉Django , 在生成静态文件的URL时 , 应该使用 / static / 作为前缀 .
例如 , 如果有一个名为style . css的CSS文件 , 并且它位于静态文件目录中 , Django会使用STATIC_URL来构建这个文件的完整URL .
在这个例子中 , style . css的URL将会是 / static / style . css .
* 2. 设置STATICFILES_DIRS ( 可选 ) .
在settings . py文件中 , TATICFILES_DIRS列表用于指定 '额外' 的静态文件目录 , 其中每个元素都是一个表示文件系统路径的字符串 .
这些目录中的文件对于整个项目都是可用的 , 而不仅仅是对于某一个特定的应用 .
Django有一个约定 , 即每个应用都可以有一个名为static的目录 , 用于存放该应用的静态文件 .
例如 , 如果有一个名为myapp的应用 , Django会自动在myapp / static / 目录下查找静态文件 .
这是Django的一个内建特性 , 不需要在STATICFILES_DIRS中指定这些目录 , Django会自动处理 .
然而 , 有时候可能希望有一些静态文件不是与特定应用关联的 , 或者想要将静态文件集中存储在一个或多个特定的目录中 .
这种情况下 , 就需要使用STATICFILES_DIRS来指定这些 '额外' 的静态文件目录 .
代码示例 :
STATICFILES_DIRS = [
BASE_DIR / 'static_extra' ,
# 其他目录 . . .
]
* 3. 在模板中引用静态文件 .
在Django模板中 , 可以使用 { % static 'path/to/your/file.ext' % } 模板标签来引用静态文件 .
Django会将STATIC_URL与提供的路径组合起来 , 生成完整的URL .
< ! -- CSS 示例 -- >
< link rel = "stylesheet" type = "text/css" href = "{% static 'css/my_styles.css' %}" >
* 4. 收集静态文件 ( 生产环境 ) .
在开发环境中 , Django的开发服务器会自动处理静态文件 .
但在生产环境中 , 需要使用collectstatic命令来收集所有静态文件到一个目录中 , 通常是STATIC_ROOT所指定的目录 .
首先 , 在settings . py中设置STATIC_ROOT .
示例 : 将所有静态文件收集到 / var / www / myproject / static / 中
STATIC_ROOT = os . path . join ( BASE_DIR , 'static_collected' )
然后 , 运行collectstatic命令 .
bash : python manage . py collectstatic , 这会将所有静态文件复制到STATIC_ROOT指定的目录中
* 5. 在Web服务器上配置静态文件服务 ( 生产环境 ) .
在生产环境中 , 通常希望Web服务器 ( 如Nginx , Apache等 ) 直接提供静态文件 , 而不是通过Django .
需要配置Web服务器以提供STATIC_ROOT目录中的文件 .
例如 , 在Nginx中 , 你可以添加一个location块来服务静态文件 ( nginx配置代码 ) :
location / static / {
alias / var / www / myproject / static_collected / ; # 与STATIC_ROOT相对应
}
* 6. 注意事项 .
- 确保你的静态文件目录结构清晰 , 避免命名冲突 .
- 在生产环境中,不要忘了运行collectstatic命令。
- 定期检查静态文件目录的权限设置 , 确保Web服务器能够读取它们 .
- 如果使用了CDN或其他缓存服务 , 确保它们正确地指向了静态文件 .
- 在开发环境中 , Django的开发服务器会自动处理静态文件 ,
但在生产环境中 , 应该使用collectstatic命令并配置Web服务器来服务静态文件 .
7.2 静态文件的引用方式
在Django模板中引用静态文件 ( 如图片 , CSS , JavaScript文件等 ) 时 ,
通常会使用 { % load static % } 模板标签来加载static模板标签库 ,
然后使用 { % static 'path/to/your/file.ext' % } 来生成静态文件的URL .
注意 : 从Django 3.1 开始 , { % get_static_prefix % } 已被弃用 , 建议使用 { % static % } 标签 .
{% load static %}
< img src = " {% static 'images/hi.jpg' %}" alt = " Hi!" />
{% load static %}
< script src = " {% static 'js/mytest.js' %}" > </ script>
{% load static %}
< link rel = " stylesheet" type = " text/css" href = " {% static 'css/my_styles.css' %}" >
{% load static %}
{% static 'images/hi.jpg' as myphoto %}
< img src = " {{ myphoto }}" alt = " Hi!" />
7.3 静态文件引用示例
下面将通过一个简单的例子来说明如何在Django项目中配置和使用静态文件 .
* 1. 创建静态文件目录 .
首先 , 在Django项目根目录下 , 新建一个名为static的文件夹 .
这个文件夹将用来存放所有的静态文件 , 包括CSS文件 , JavaScript文件和图片文件 .
* 2. 配置静态文件设置 .
在Django项目的settings . py文件中 , 配置STATIC_URL和STATICFILES_DIRS , 以便Django知道如何找到和服务静态文件 .
STATIC_URL = '/static/'
STATICFILES_DIRS = [
BASE_DIR / 'static' ,
]
* 3. 创建静态文件 .
在static目录下创建CSS和JavaScript文件 .
例如 , 创建一个mycss . css文件来定义一些样式 , 和一个myjs . js文件来编写一些JavaScript代码 .
h4 {
color : red;
}
这段代码将设置h4标签的文字为红色 .
var h4Elements = document. querySelectorAll ( 'h4' ) ;
h4Elements. forEach ( function ( h4 ) {
h4. addEventListener ( 'click' , function ( ) {
this . style. color = 'green' ;
} ) ;
} ) ;
这段代码首先使用querySelectorAll方法获取页面上所有的 < h4 > 元素 , 并为它们每个都添加一个点击事件监听器 .
当某个 < h4 > 元素被点击时 , 它的文本颜色会被更改为绿色 .
* 4. 在HTML模板中引用静态文件 .
在Django模板 ( 例如index . html ) 中 , 使用配置的STATIC_URL来引用静态文件 .
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> 静态文件示例</ title>
{% load static %}
< link rel = " stylesheet" href = " {% static 'mycss.css' %}" >
</ head>
< body>
< h4> 我是红色, 点击变绿</ h4>
< script src = " {% static 'myjs.js' %}" > </ script>
</ body>
</ html>
在示例中 , 使用了 { % static % } 标签来正确地生成静态文件的URL .
这样 , Django就会在运行时将 { % static % } 标签替换为正确的静态文件路径 .
这个过程是这样的 :
Django会查找项目中设置的STATIC_URL , settings . py文件中的默认定义 : STATIC_URL = '/static/' .
然后 , Django会将 { % static 'myjs.js' % } 中的 'myjs.js' 与STATIC_URL拼接起来 , 形成完整的静态文件URL , 比如 / static / myjs . js .
最后 , 这个URL会被插入到模板中 , 替换掉 { % static 'myjs.js' % } .
所以 , 模板中使用 { % static 'myjs.js' % } 时 , 实际上会被替换为 / static / myjs . js ( 或者settings . py中为STATIC_URL设置的其他值 ) .
这样 , 浏览器就可以正确地加载静态文件了 .
配置路由与视图函数 :
from django. urls import path
from index. views import my_view
urlpatterns = [
path( 'my_view/' , my_view, name= 'my_view' ) ,
]
from django. shortcuts import render
def my_view ( request) :
return render( request, 'index.html' )
启动项目 , 访问 : 127.0 .0 .1 : 8000 /index/my_view/ , 此时看到一个红色的 < h4 > 标签 , 当点击它时 , 它的颜色会变成绿色 .
7.4 静态文件组织策略
Django的static文件夹用于存放静态文件 , 如CSS , JavaScript , 图片等 , 这些文件在Web应用中通常用于提供样式 , 功能和视觉效果 .
与Django的视图 , 模型 , 表单等代码不同 , 静态文件并不直接参与应用的逻辑处理 , 而是通过HTTP请求被客户端 ( 如浏览器 ) 下载和使用 .
对于小型Django项目 , 将所有静态文件放在一个static文件夹内可能是可行的 , 特别是当项目规模较小且静态文件数量不多时 .
这样做的好处是简单和直观 , 便于管理和访问 .
然而 , 即使在小型项目中 , 也建议按照某种逻辑 ( 如按应用 , 按功能等 ) 来组织静态文件 , 以保持项目的整洁和可扩展性 .
对于大型Django项目 , 静态文件的细分和组织变得尤为重要 .
随着项目规模的增大 , 静态文件的数量和种类也会相应增加 . 如果不加以组织和细分 , 静态文件夹可能会变得杂乱无章 , 难以维护 .
在大型项目中 , 可以采取以下策略来组织静态文件 :
* 1. 按应用划分 : 每个Django应用都可以有自己的static文件夹 , 用于存放该应用特有的静态文件 .
这样做有助于将静态文件与它们所属的应用关联起来 , 便于管理和复用 .
* 2. 按资源类型分类 :
- 创建一个 'css' 文件夹来存放所有的CSS文件 .
- 创建一个 'js' 文件夹来存放所有的JavaScript文件 .
- 创建一个 'images' 文件夹来存放所有的图片文件 .
- 如果有的话 , 还可以创建 'fonts' 文件夹来存放字体文件 , 'videos' 文件夹来存放视频文件等 .
* 3. 按页面或组件分类 : 如果网站或应用有多个页面或组件 , 可以考虑在 'static' 文件夹下为每个页面或组件创建一个单独的子文件夹 .
例如 , 如果网站有一个 '首页' 和一个 '联系我们' 页面 , 可以创建 'home' 和 'contact' 子文件夹 ,
并将与这些页面相关的静态资源分别放在这两个文件夹中 .
* 4. 使用版本控制 : 如果项目有多个版本 , 或者想要跟踪静态资源的变化 , 可以考虑在文件名或文件夹名中加入版本号 .
例如 , 可以将旧版本的CSS文件命名为 'style-v1.css' , 而将新版本的CSS文件命名为 'style-v2.css' .
* 5. 利用静态文件管理工具 : 在大型项目中 , 考虑使用专门的静态文件管理工具 ( 如Webpack , Gulp等 ) 来优化 , 打包和压缩静态文件 ,
以提高应用的加载速度和性能 .
总之 , 无论项目大小 , 都应该注重静态文件的组织和细分 .
在Django项目中 , 通过合理的组织和命名规则 , 以及利用Django提供的静态文件管理机制 , 可以确保静态文件的易读性 , 可维护性和可扩展性 .
7.5 CDN服务介绍
CDN ( Content Delivery Network , 内容分发网络 ) 服务 : 是一种通过在网络各处放置节点服务器来加速内容传输的技术 .
以下是对CDN服务的详细归纳和解释 :
* 1. CDN的基本概念 : 它是一种建立并覆盖在现有网络基础设施之上的智能虚拟网络 ,
旨在通过在网络各处部署节点服务器 , 使用户能够就近获取所需内容 , 从而提高内容传输的速度和稳定性 .
* 2. CDN的主要优势 :
- 提高访问速度 : CDN通过将内容缓存在全球多个节点服务器上 ,
使得用户能够就近访问这些内容 , 从而显著减少数据传输的延迟和响应时间 .
- 减轻源站压力 : CDN能够处理大量的静态资源请求 , 从而减轻源服务器的负载 , 使其能够更专注于处理动态内容和其他核心任务 .
- 提高可扩展性 : 随着用户量的增长 , CDN可以轻松地通过增加更多的节点服务器来扩展其容量和覆盖范围 , 以满足不断增长的需求 .
- 增强安全性 : CDN通常提供HTTPS支持 , DDoS防护等安全功能 , 帮助保护网站免受恶意攻击和未经授权的访问 .
* 3. CDN的工作原理 , CDN的工作原理主要包括以下几个步骤 :
1. 用户发起请求 : 当用户访问网站时 , 浏览器会向CDN系统发起对内容的请求 .
2. 智能调度 : CDN系统会根据用户的地理位置 , 网络状况等因素 , 智能地选择一个离用户最近且负载较低的节点服务器来响应用户的请求 .
3. 内容缓存 : 如果节点服务器中已经缓存了用户请求的内容 , 则会直接将该内容返回给用户 ;
如果没有缓存 , 则会向源服务器请求内容 , 并将其缓存到节点服务器上以供后续使用 .
4. 内容传输 : 最后 , 节点服务器会将缓存的内容传输给用户的浏览器进行展示 .
* 4. CDN的应用场景 , CDN服务广泛应用于各种需要加速内容传输的场景中 , 包括但不限于 :
- 网站加速 : 通过CDN加速网站的静态资源 ( 如图片 , CSS , JavaScript等 ) , 提高网站的访问速度和用户体验。
- 视频分发:CDN可以将视频内容缓存在全球多个节点服务器上,使得用户能够流畅地观看高清视频内容。
- 游戏加速:CDN可以降低游戏数据的传输延迟和丢包率,提高游戏的流畅度和稳定性。
- 电商平台:CDN可以加速电商平台的商品图片、商品详情页等内容的传输速度,提升用户的购物体验。
总之 , CDN服务是一种高效 , 可靠的内容分发技术 , 通过在网络各处放置节点服务器来加速内容传输的速度和稳定性 .
在Django项目中直接使用CDN与Django本身提供的静态文件服务相比 , 在性能上通常会有显著的提升 ,
特别是当网站或应用需要处理大量的静态资源 , 或者目标用户分布在全球各地时 .
以下是使用CDN相对于Django自带静态文件服务的几个主要优势 :
* 1. 更快的加载速度 : CDN通过在全球多个地点部署服务器 ( 称为边缘节点 ) , 将静态资源缓存在这些节点上 .
当用户请求静态资源时 , CDN会选择一个离用户最近的节点来提供资源 , 从而显著减少传输延迟和加载时间 .
相比之下 , Django自带的静态文件服务通常只部署在单个或少数几个服务器上 , 无法提供相同级别的全球覆盖和性能优化 .
* 2. 减轻服务器负载 : 由于CDN能够处理大量的静态资源请求 , 因此可以减轻Django服务器的负载 .
Django服务器可以专注于处理更复杂的动态内容生成和数据库查询等任务 , 从而提高整体性能和响应速度 .
* 3. 更好的可扩展性 : 随着网站或应用的用户量增长 , CDN可以轻松地通过增加更多的边缘节点来扩展其容量和覆盖范围 .
相比之下 , Django自带的静态文件服务可能需要更多的服务器和复杂的负载均衡配置来应对增长的流量 .
* 4. 增强的安全性 : CDN通常提供额外的安全功能 , 如HTTPS支持 , DDoS防护和Web应用防火墙 ( WAF ) 等 .
这些功能可以帮助保护静态资源免受恶意攻击和未经授权的访问 .
然而 , 需要注意的是 , 使用CDN并不意味着可以完全放弃Django自带的静态文件服务 .
在开发环境中 , 仍然需要使用Django来提供静态文件 , 以便进行开发和测试 .
但在生产环境中 , 可以将静态文件部署到CDN上 , 并通过修改Django的设置来引用CDN上的URL .
为了将Django项目中的静态文件部署到CDN上 , 通常需要执行以下步骤 :
* 1. 将静态文件收集到一个统一的目录中 ( 使用Django的collectstatic命令 ) .
* 2. 将这些文件上传到CDN提供商的存储桶或容器中 .
* 3. 修改Django的设置文件 ( 如settings . py ) , 将静态文件的URL设置为CDN提供的URL .
* 4. 确保在HTML模板和其他需要引用静态文件的地方使用正确的URL .
总之 , 使用CDN可以显著提高Django项目中静态资源的加载速度和性能 , 特别是在处理大量静态资源或目标用户分布广泛的情况下 .
然而 , 这并不意味着可以完全放弃Django自带的静态文件服务 , 而是在生产环境中将静态文件部署到CDN上以提高性能和可扩展性 .