26.5 Django模板层

2024-07-03_013522

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)来访问这些值.
     模板引擎内部会解析模板字符串, 查找所有的变量引用, 然后将这些引用替换为上下文中相应键的值.
# MyDajngo的settings.py
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',
            ],
        },
    },
]

2024-06-25_130839

在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'来实现.
# MyDjango的urls.py 主路由
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'))
]

# index的urls.py 子路由
from django.urls import path
from index.views import current_datetime

urlpatterns = [
    path('current_datetime/', current_datetime, name='current_datetime')
]

image-20240625131746602

# index的views.py
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)

image-20240625131018797

在上面的例子中, current_datetime.html是模板文件的名称, 它应该位于配置的模板文件夹中.
context字典包含了要在模板中使用的变量.
<!-- templates的current_datetime.html-->
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>当前时间</title>
    </head>
    <body>
        <h2> 现在的时间为: {{ current_datetime }}. </h2>
    </body>
</html>
在这个例子中, {{ current_date }}将被替换为传递给模板的current_date变量的值.

image-20240625131722788

启动项目, 访问: 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 变量使用示例

以下是一个使用模板语法变量的简单示例:
# index的urls.oy
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'),

]

image-20240626124120176

# index的views.py
from django.template import Template, Context  
from django.http import HttpResponse  
  
def template_variables(request):  
    # 假设有一个名为 context 的字典,包含了要传递给模板的变量  
    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 和 Context 类从字符串中渲染模板  
    template = Template(template_str)  
    rendered_template = template.render(Context(context))  
  
    # 返回一个 HttpResponse 对象,包含渲染后的模板内容  
    return HttpResponse(rendered_template)

image-20240626124146105

这里, 使用了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/ , 结果如下:

image-20240626124246763

尝试访问不存在的属性, 但存在同名方法示例: Person类没有age属性, 但有一个名为age的方法:
# index的views.py
def template_test(request):
    name = 'kid'

    class Person:
        def __init__(self, name):
            self.name = name

        def age(self):
            return 30  # 假设每个人的年龄都是30岁, 这只是一个示例

    # 实例化对象
    qq = Person('qq')
    qz = Person('qz')
    person_list = [qq, qz]

    return render(request, 'template_test.html', locals())

locals()函数返回的是当前局部符号表的字典, 它包含了当前作用域内的所有局部变量.

image-20240626205236930

在模板中, 访问age属性: <p>{{ person.age }}</p>
<!-- templates的filter_examples.html -->
<!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>

image-20240626205559170

模板引擎会首先尝试查找age属性, 但找不到.
然后, 由于age实际上是一个方法, 模板引擎可能会尝试调用这个方法(这取决于模板引擎的实现和配置).
添加template_test视图的路由:
# index的urls.py
from django.urls import path
from index.views import current_datetime, template_variables, template_test

urlpatterns = [
    # ...
    path('template_test/', template_test, name='template_test'),

]

image-20240626212109182

启动项目, 访问: http://127.0.0.1:8000/index/template_test/ , 结果如下.

image-20240626205126548

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过滤器来格式化这些数据.
# index的urls.py
from django.urls import path
from index.views import filter_examples

urlpatterns = [
    # ...
    path('filter_examples/', filter_examples, name='filter_examples'),

]

image-20240627172015421

# index的views.py
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)

image-20240627172124161

<!-- templates的template_test.html -->
<!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> <!-- 截断到5个单词 -->
<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> <!-- 例如: Tuesday, March 15, 2023, 3:04 PM EST -->

</body>
</html>

image-20240627172156138

启动项目, 访问: http://127.0.0.1:8000/index/filter_examples/ , 结果如下:

image-20240627172357091

3.3 HTML标签转义

默认情况下, Django会对所有的变量进行HTML转义, 以防止跨站脚本攻击(XSS).
跨站脚本攻击(Cross-Site Scripting, 简称 XSS)是一种安全漏洞, 它允许攻击者在受害者的浏览器上执行恶意脚本(后续说).
这意味着, 如果有一个变量包含HTML标签, Django会将这些标签转换为它们的HTML实体形式, 从而防止它们被浏览器解析为真实的HTML.
# view.py
def my_view(request):
    my_html = "<p>这是一段HTML内容. </p>"
    return render(request, 'my_template.html', {'safe_html': my_html})

image-20240629134512172

在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函数在功能上相似, 它告诉模板引擎不要对变量进行转义.
# index的urls.py
from django.urls import path
from index.views import my_view

urlpatterns = [
    path('my_view/', my_view, name='my_view'),
]

image-20240629135318012

# index的views.py
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)

image-20240629135628913

<!-- templates的my_template.html-->
<!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>

image-20240629145016271

启动项目, 访问: http://127.0.0.1:8000/index/my_view/ , 结果如下:

image-20240629144903452

4. 模版语法之标签

在Django模板中, 标签(tags)是用于执行逻辑或循环结构的指令, 它们确实通常被包裹在{%  %}之间.
Django模板标签提供了很多功能, 包括但不限于控制流, 循环, 加载静态文件, 包含其他模板等.
使用需要明确开始和结束标签的模板标签时, 就需要闭合标签. 这些标签通常用于控制流(如条件判断和循环)或包含其他模板内容.

4.1 常用标签介绍

下面是一些Django模板中常用的标签示例:
* 1. for标签: 用于在模板中遍历列表, 元组, 字典等可迭代对象.
<ul>  
    {% for item in items %}  <!-- 开始标签 -->
    <li>{{ item }}</li>  
    {% endfor %}  <!-- 闭合标签 -->  
</ul>
<!-- 如果items是一个列表['apple', 'banana', 'cherry'], 上面的模板将会渲染出包含这三个元素的列表. -->

{% for item in items %}
    <p>{{ item }}</p>
{% endfor %}

<!-- 可以利用{% for item in items reversed %} 反向完成循环  -->


{% for key,val in dic.items %}
    <p>{{ key }}:{{ val }}</p>
{% endfor %}
<!-- 遍历对象, python对象可以使用内置方法(不用加括号, 会自动调用) -->


<!-- for 标签带有一个可选的{% empty %}从句,  以便在给出的组是空的或者没有被找到时, 可以有所操作 -->
{% 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视图, 并传递一个包含人员列表的上下文到模板中.
# index的views.py
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)

image-20240628024039649

然后, 在模板文件person_list.html中, 可以使用for标签来遍历人员列表, 并显示每个人的名字.
同时, 使用{% empty %}来处理列表为空的情况:
<!-- templates的person_list.html -->
<!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>
        <!-- 显示循环的序号(从1开始)-->
        <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>
        <!-- 显示循环的序号(从1开始) -->
        <p>Iteration: {{ forloop.counter }}</p>
    {% empty %}
        <li>Sorry, no person here</li>
    {% endfor %}
</ul>
</body>
</html>

image-20240628024118857

最后, 在urls.py文件中添加一个URL模式来映射视图:
# index的urls.py
from django.urls import path
from index.views import person_list

urlpatterns = [
    # ...
    path('person_list/', person_list, name='person_list'),

]

image-20240628024009160

启动项目, 访问: 127.0.0.1:8000/index/person_list/ .
模板中如果person_list列表不为空, 它将显示每个人的名字和循环的序号; 如果列表为空, 它将显示'Sorry, no person here'.

image-20240628023724260

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表格中显示它们, 同时添加一些额外的信息, 
如当前循环的索引和是否是第一次或最后一次迭代.
首先, 确保视图返回了一个包含列表的上下文.
# index的views.py
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)

image-20240628031548592

在模板中, 使用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>

image-20240628033220787

在这个模板中, forloop.counter和forloop.counter0分别显示了基于1和基于0的当前迭代索引.
forloop.revcounter和forloop.revcounter0则显示了从循环末尾开始的迭代索引.
forloop.first和forloop.last则用于判断当前迭代是否是第一次或最后一次.
使用yesno模板过滤器, 将forloop.first和forloop.last的布尔值转换为更友好的"True""False"字符串.
添加路由, 确保URL配置映射到这个视图.
# index的urls.py
from django.urls import path  
from index.views import forloop_example  
  
urlpatterns = [  
    # ... 其他URL模式 ...  
    path('example/', forloop_example, name='forloop_example'),  
]

image-20240628033332605

启动项项目: 访问: 127.0.0.1:8000/index/forloop_example ,
然后会看到一个表格, 其中列出了每个项目的详细信息, 包括循环的各种索引和是否是第一次或最后一次迭代.

image-20240628031354945

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包.

image-20240629084249679

* 步骤2: 编写自定义模板标签(my_tags.py).
# index/templatetags/urls.py
# 从django包中导入了template模块, 该模块包含了用于创建自定义模板标签和过滤器的工具.
from django import template  

# 创建了一个名为register的模板标签库对象, 用于注册自定义的模板标签和过滤器.
register = template.Library()  # register的名字是固定的, 不可改变


# 装饰器: 用于创建自定义的模板标签.
@register.simple_tag  
def hello_world():  
    return "Hello, World!" 


# 装饰器: 用于创建自定义的模板标签.
@register.filter
def add_value(value1, value2):
    return value1 + value2

image-20240629090142178

@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 %}. 然后, 再使用自定义标签.
# index的urls.py
from django.urls import path
from index.views import my_tags

urlpatterns = [
    path('my_tags/', my_tags, name='my_tags'),
]

image-20240629090547082

# index的views.py
from django.shortcuts import render


def my_tags(request):
    return render(request, 'index.html')

image-20240629090613366

<!--templates的index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>自定义标签</title>

</head>
<body>
<!-- 加载自定义标签 -->
{% load my_tags %}
<!-- 使用simple_tag -->
{% hello_world %}
<!-- 使用filter -->
{{ 1 | add_value:2 }}
</body>
</html>

image-20240629090525401

启动项目, 访问: http://127.0.0.1:8000/index/my_tags/ , 结果如下:

image-20240629090700233

5.2 多参数处理

在Django的模板系统中, 标准的过滤器通常只接受一个模板变量和一个可选的参数.
直接在一个过滤器中传递三个参数(即两个额外的参数)是不支持的.

如果需要处理多个参数, 并且这些参数之间的关系比较复杂, 那么使用自定义模板标签可能是一个更好的选择.
自定义模板标签可以接受任意数量的参数, 并在模板中生成输出.
使用上面的示例, 修改部分文件的代码用于新的测试:
# index/templatetags/urls.py
from django import template  
  
register = template.Library()  

# 在takes_context=True的情况下, 可以访问模板的上下文变量.

@register.simple_tag(takes_context=True)
def custom_tag(context, arg1, arg2, arg3):
    # 在这里可以使用 context 变量, 以及 arg1, arg2, arg3
    # ... 执行你的逻辑 ...
    # context是一个字典
    return context['num'] + arg1 + arg2 + arg3

image-20240629123349661

# index的views.py
from django.shortcuts import render


def my_tags(request):
    context = {
        'num': 1
    }

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

image-20240629123158960

在模板中, 可以这样使用:
{% load my_tags %}  
{% custom_tag arg1 arg2 arg3 %}
<!--templates的index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>自定义标签</title>

</head>
<body>
<!-- 加载自定义标签 -->
{% load my_tags %}

{% custom_tag 2 3 4 %}
</body>
</html>

image-20240629123229903

启动项目, 访问: http://127.0.0.1:8000/index/my_tags/ , 结果如下(1 + 2 + 3 + 4 = 10):

image-20240629123634309

5.3 创建可重用模板片段

inclusion_tag是Django模板标签库中的一个装饰器, 用于创建可重用的模板片段.
这些片段可以接收参数, 并根据这些参数动态生成HTML内容.
在templatetags/my_inclusion.py中定义inclusion_tag:
# index/templatetags/my_inclusion.py
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}

image-20240630003029955

创建templates/result.html模板:
<!-- templates的result.html -->
<ul>  
  {% for choice in data %}  
    <li>{{ choice }}</li>  
  {% endfor %}  
</ul>

image-20240630003108699

在templates/index.html模板中使用show_results.
注意, 需要先在模板的顶部加载自定义的标签库!
<!-- templates的index.html -->
{% load my_inclusion %}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>包含标签</title>
</head>
<body>
{% show_results 10 %}
</body>
</html>

image-20240630003210200

编写路由与视图函数, 当用户访问时返回index.html页面.
# index的urls.py
from django.urls import path
from index.views import my_view

urlpatterns = [
    path('my_view/', my_view, name='my_view'),
]

# index的views.py
from django.shortcuts import render


def my_view(request):
    return render(request, 'index.html')

image-20240630003538852

当渲染index.html时, Django会调用show_results函数, 并传入参数10.
这个函数会生成一个包含10个列表项的HTML列表, 并将其插入到index.html中{% show_results 10 %}的位置.
启动项目: 访问: http://127.0.0.1:8000/index/my_view/ , 结果如下:

image-20240630002917884

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 %} 来定义块. 这些块是占位符, 用于在子模板中提供具体的内容.
<!-- templates的base.html(基础模板) -->
<!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>

image-20240629165623950

子模板使用{% extends %}标签来声明它继承自哪个父模板.
然后使用相同的 {% block %} 标签和相同的名字来覆盖这个块, 重新定义父模板中的块来提供具体的内容. 
<!-- templates的home.html(继承自 base.html 的子模板) -->

{% 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 %}

image-20240629165603132

在上面的示例中, home.html继承了base.html, 并覆盖了title和content块.
编写路由和视图用于测试模板继承:
# index的urls.py
from django.urls import path
from index.views import my_view

urlpatterns = [
    path('my_view/', my_view, name='my_view'),
]

# index的views.py
from django.shortcuts import render


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

image-20240629165852848

启动项目, 访问: http://127.0.0.1:8000/index/my_view/ , 结果如下, 继承模板并替换网站标题和网站内容:

2024-06-29_165953

在Django的模板系统中, {% block.super %}是一个特殊的模板标签, 
它允许子模板在覆盖或扩展父模板中的块时, 保留父模板中该块的原始内容.
这个标签在模板继承中特别有用, 当想在父模板的默认内容之上或之后添加一些额外的内容时.

当使用 {% block %}  {% endblock %} 在父模板中定义一个块时, 可以提供一个默认的内容.
在子模板中, 可以使用相同的 {% block %} 标签和相同的名字来覆盖这个块.
然而, 如果还想保留父模板中该块的原始内容, 可以使用 {% block.super %}.
沿用上面的示例修改部分代码, 列举一个简单的例子来解释{% block.super %}的用法:
# index的views.py
from django.shortcuts import render


def my_view(request):
    return render(request, 'child.html',)

image-20240629182009070

<!-- templates的parent.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>

image-20240629182141135

<!-- templates的child.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 %}

image-20240629182202566

在这个例子中, 子模板child.html继承了父模板base.html.
它覆盖了title块和content块.
在content块中, 它使用了{{ block.super }}来保留父模板中content块的原始内容, 并在其后添加了一段新的内容.
启动项目, 访问: http://127.0.0.1:8000/index/my_view/ , 
可以看到{{ block.super }}允许子模板在覆盖块时保留父模板中的原始内容.

2024-06-29_181822

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 %} 标签来包含这个侧边栏:
<!-- templates的sidebar.html -->  
<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>

image-20240629200302341

在这个示例中, sidebar.html包含了一个带有标题的侧边栏, 
标题通过 {{ sidebar_title }} 变量来动态设置侧边栏内部有一个无序列表, 列出了几个链接,
可以根据需要添加更多的链接或其他内容.

在父模板( base.html), 可以使用 {% include "sidebar.html" with sidebar_title="我的侧边栏" %} 来包含这个侧边栏模板,
并传递一个 sidebar_title 变量来设置侧边栏的标题.
这样, 每次渲染父模板时, 侧边栏的内容都会根据传递的变量动态生成.

传递变量到包含的模板: 可以使用with选项向包含的模板传递额外的上下文变量.
这些变量只在被包含的模板中可用.
<!-- templates的base.html -->  
<!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>

image-20240629200339152

在这个例子中, base.html是主模板, 它使用 {% include "sidebar.html" %} 来包含sidebar.html模板的内容.
当Django渲染base.html时, 它会找到并渲染sidebar.html模板, 然后将结果插入到base.html中的相应位置.
修改上面的示例的视图函数, 返回base.html页面.
# index的views.py
from django.shortcuts import render


def my_view(request):
    context = {
        'message': 'My Django',
        # 其他上下文变量...
    }
    return render(request, 'base.html', context)

image-20240629200407329

启动项目, 访问: http://127.0.0.1:8000/index/my_view/ , 结果如下,

image-20240629200228848

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!" />
<!-- 引用JavaScript文件 -->
{% load static %}  
<script src="{% static 'js/mytest.js' %}"></script>
<!-- 引用CSS文件 -->
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'css/my_styles.css' %}"> 
<!-- 将静态文件URL存储为变量 -->
{% load static %}  
{% static 'images/hi.jpg' as myphoto %}  
<img src="{{ myphoto }}" alt="Hi!" />

7.3 静态文件引用示例

下面将通过一个简单的例子来说明如何在Django项目中配置和使用静态文件.
* 1. 创建静态文件目录.
     首先, 在Django项目根目录下, 新建一个名为static的文件夹.
     这个文件夹将用来存放所有的静态文件, 包括CSS文件, JavaScript文件和图片文件. 

image-20240702224219318

* 2. 配置静态文件设置.
     在Django项目的settings.py文件中, 配置STATIC_URL和STATICFILES_DIRS, 以便Django知道如何找到和服务静态文件.
# MyDjango的settings.py
STATIC_URL = '/static/'  # 设置静态文件的URL前缀

STATICFILES_DIRS = [
    BASE_DIR / 'static',  # 添加静态文件目录的路径
]

image-20240703004258378

* 3. 创建静态文件.
     在static目录下创建CSS和JavaScript文件.
     例如, 创建一个mycss.css文件来定义一些样式, 和一个myjs.js文件来编写一些JavaScript代码.
/* static的mycss.css */
h4 {  
    color: red;  
}
这段代码将设置h4标签的文字为红色.
//  static的myjs.js
// 获取所有的 h4 元素  
var h4Elements = document.querySelectorAll('h4');  
  
// 为每个 h4 元素添加点击事件监听器  
h4Elements.forEach(function(h4) {  
    h4.addEventListener('click', function() {  
        // 当 h4 被点击时,更改其颜色为绿色  
        this.style.color = 'green';  
    });  
});
这段代码首先使用querySelectorAll方法获取页面上所有的<h4>元素, 并为它们每个都添加一个点击事件监听器.
当某个<h4>元素被点击时, 它的文本颜色会被更改为绿色.

image-20240703004928024

* 4. 在HTML模板中引用静态文件.
     在Django模板(例如index.html), 使用配置的STATIC_URL来引用静态文件.
<!-- templates的index -->
<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta charset="UTF-8">  
    <title>静态文件示例</title>  
    {% load static %}  
    <!-- 引用CSS文件 -->  
    <link rel="stylesheet" href="{% static 'mycss.css' %}">  
</head>  
<body>  
    <h4>我是红色, 点击变绿</h4>  
    <!-- 引用JavaScript文件 -->  
    <script src="{% static 'myjs.js' %}"></script>  
</body>  
</html>

image-20240703010104399

在示例中, 使用了{% 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设置的其他值).
这样, 浏览器就可以正确地加载静态文件了.
配置路由与视图函数:
# index的urls.py
from django.urls import path
from index.views import my_view

urlpatterns = [
    path('my_view/', my_view, name='my_view'),
]

# index的views.py
from django.shortcuts import render


def my_view(request):
    return render(request, 'index.html')

image-20240703010542528

启动项目, 访问: 127.0.0.1:8000/index/my_view/ , 此时看到一个红色的<h4>标签, 当点击它时, 它的颜色会变成绿色.

GIF 2024-7-3 1-07-30

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

推动能源绿色低碳发展,风机巡检进入国产超高清+AI时代

全球绿色低碳能源数字转型发展正在进入一个重要窗口期。风电作为一种清洁能源&#xff0c;在碳中和过程中扮演重要角色&#xff0c;但风电场运维却是一件十足的“苦差事”。 传统的风机叶片人工巡检方式主要依靠巡检人员利用高倍望远镜检查、高空绕行下降目测检查(蜘蛛人)、叶…

校园水质信息化监管系统——水质监管物联网系统

随着物联网技术的发展越来越成熟&#xff0c;它不断地与人们的日常生活和工作深入融合&#xff0c;推动着社会的进步。其中物联网系统集成在高校实践课程中可以应用到许多项目&#xff0c;如环境气象检测、花卉种植信息化监管、水质信息化监管、校园设施物联网信息化改造、停车…

Qt6 qcustomplot在图表上画一条直线

完整代码如下: 主要注意的是Qt中的QHBoxLayout等Qt类对象在被引用的情况下是可以使用局部变量的,典型的如setLayout这类型的函数接口,都可以使用局部变量,而不是new对象。 另外一点就是qcustomplot中的replot就相当于Qt中的update,由于qcustomplot是属于绘图类的接口库,…

如何用Python向PPT中批量插入图片

办公自动化办公中&#xff0c;Python最大的优势是可以批量操作&#xff0c;省去了用户粘贴、复制、插入等繁琐的操作。经常做PPT的朋友都知道&#xff0c;把图片插入到PPT当中的固定位置是一个非常繁琐的操作&#xff0c;往往调整图片时耗费大量的时间和精力。如何能省时省力插…

新型200V预稳压器可简化故障容受型电源的设计

讨论几种设计故障容受型电源的方法&#xff0c;其中包括新的预稳压器拓扑结构&#xff0c;该结构可简化电路设计及元件选择。 对抗相位故障 如果交流电源到电表之间出现错误连接故障&#xff0c;或是像空调或电磁炉等采用三相电源工作的大功率负载在两个相位之间的连接错误&a…

微信小程序 canvas 处理图片的缩放移动旋转问题

这里使用到了一个插件&#xff0c;canvas-drag&#xff0c;来实现大部分功能的 上效果 直接上代码吧~ wxml <div class"container"><canvas-drag id"canvas-drag" graph"{{graph}}" width"700" height"750" ena…

[漏洞分析] CVE-2024-6387 OpenSSH核弹核的并不是很弹

文章目录 漏洞简介漏洞原理补丁分析漏洞原理 漏洞利用漏洞利用1: SSH-2.0-OpenSSH_3.4p1 Debian 1:3.4p1-1.woody.3 (Debian 3.0r6, from 2005) [无ASLR无NX]漏洞利用原理漏洞利用关键点 漏洞利用2: SSH-2.0-OpenSSH_4.2p1 Debian-7ubuntu3 (Ubuntu 6.06.1, from 2006) [无ASLR…

[C++][设计模式][组合模式]详细讲解

目录 1.动机(Motivation)2.模式定义3.要点总结4.代码感受 1.动机(Motivation) 软件在某些情况下&#xff0c;客户代码过多地依赖于对象容器复杂的内部实现结构&#xff0c;对象容器内部实现结构(而非抽象结构)的变化引起客户代码的频繁变化&#xff0c;带来了代码的维护性、扩…

Hi3861 OpenHarmony嵌入式应用入门--wifi sta

鸿蒙WiFi STA模式相关的API接口文件路径 foundation/communication/interfaces/kits/wifi_lite/wifiservice/wifi_device.h 所使用的API接口有&#xff1a; API 接口说明 WifiErrorCode EnableWifi(void); 开启STA WifiErrorCode DisableWifi(void); 关闭STA int IsWif…

《后端程序猿 · 基于 Lettuce 实现缓存容错策略》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; 近期刚转战 CSDN&#xff0c;会严格把控文章质量&#xff0c;绝不滥竽充数&#xff0c;如需交流&#xff…

大数据期末复习——hadoop、hive等基础知识

一、题型分析 1、Hadoop环境搭建 2、hadoop的三大组件 HDFS&#xff1a;NameNode&#xff0c;DataNode&#xff0c;SecondaryNameNode YARN&#xff1a;ResourceManager&#xff0c;NodeManager &#xff08;Yarn的工作原理&#xff09; MapReduce&#xff1a;Map&#xff0…

点云处理实操 点云平面拟合

目录 一、什么是平拟合 二、拟合步骤 三、数学原理 1、平面拟合 2、PCA过程 四、代码 一、什么是平拟合 平面拟合是指在三维空间中找到一个平面,使其尽可能接近给定的点云。最小二乘法是一种常用的拟合方法,通过最小化误差平方和来找到最优的拟合平面。 二、拟合步骤…

Kafka 为何如此之快?深度解析其背后的秘密

目录 前言 一、生产者 1. 异步发送 2. 多分区并行 3. 消息批量发送 4.支持消息压缩 二、存储端 1. 分区和副本 2. 页缓存 3. 磁盘顺序写入 4. 零拷贝技术 5. 稀疏索引 三、消费端 1. 消费者群组 2. 批量拉取 3. 高效的偏移量管理 4. 并行消费 总结 前言 Kafk…

观测云赋能「阿里云飞天企业版」,打造全方位监控观测解决方案

近日&#xff0c;观测云成功通过了「阿里云飞天企业版」的生态集成认证测试&#xff0c;并荣获阿里云颁发的产品生态集成认证证书。作为监控观测领域的领军者&#xff0c;观测云一直专注于提供统一的数据视角&#xff0c;助力用户构建起全球范围内的端到端全链路可观测服务。此…

SwanLinkOS首批实现与HarmonyOS NEXT互联互通,软通动力子公司鸿湖万联助力鸿蒙生态统一互联

在刚刚落下帷幕的华为开发者大会2024上&#xff0c;伴随全场景智能操作系统HarmonyOS Next的盛大发布&#xff0c;作为基于OpenHarmony的同根同源系统生态&#xff0c;软通动力子公司鸿湖万联全域智能操作系统SwanLinkOS首批实现与HarmonyOS NEXT互联互通&#xff0c;率先攻克基…

Appium adb 获取appActivity

方法一&#xff08;最简单有效的方法&#xff09; 通过cmd命令&#xff0c;前提是先打开手机中你要获取包名的APP adb devices -l 获取连接设备详细信息 adb shell dumpsys activity | grep mFocusedActivity 有时获取到的不是真实的Activity 方法二 adb shell monkey -p …

Java中反射的使用

无参构造器 方法的调用 package com.studio;import java.lang.reflect.Method;class User {private String name;/*无参构造器*/public User() {}public String getName() {return name;}public void setName(String name) {this.name name;}Overridepublic String toString…

PHP景区旅游多商户版微信小程序系统源码

解锁景区新玩法&#xff01;​ 引言&#xff1a;一站式旅行新体验 厌倦了传统景区的单调游览&#xff1f;想要一次旅行就能体验多种风情&#xff1f;那么&#xff0c;“景区旅游多商户版”绝对是你的不二之选&#xff01;这个创新模式将景区内多个商户资源整合&#xff0c;为…

C# WPF自制批注工具(方便标记重点和演示)

在教学和演示中&#xff0c;我们通常需要对重点进行批注&#xff0c;下载安装第三方工具批注显得很麻烦。本篇使用WPF开发了一个批注工具&#xff0c;工具小巧&#xff0c;功能丰富&#xff0c;非常使用日常免费使用&#xff0c;或者进行再次开发。 自制批注工具具有以下功能特…

自动驾驶水泥搅拌车在梁场的应用(下)

自动驾驶水泥搅拌车在梁场的应用&#xff08;下&#xff09; 北京渡众机器人科技有限公司的自动驾驶水泥搅拌车在梁场&#xff08;也称为预制梁场&#xff09;的应用可以带来多方面的优势和效益&#xff1a; 1. 自动化搅拌和运输 在梁场中&#xff0c;通常需要大量的混凝土搅…