Django-模板(模板语言)

模板

作为Web 框架,Django 需要一种很便利的方法以动态地生成HTML。最常见的做法是使用模板。模板包含所需HTML 输出的静态部分,以及一些特殊的语法,描述如何将动态内容插入。
Django 项目可以配置一个或多个模板引擎(甚至是零,如果你不需要使用模板)。Django 的模板系统自带内建的后台 —— 称为Django 模板语言(DTL),以及另外一种流行的Jinja2。其他的模板语言的后端,可查找第三方库。

配置模板引擎

模板引擎通过TEMPLATES 设置来配置。它是一个设置选项列表,与引擎一一对应。默认的值为空。由startproject 命令生成的settings.py 定义了一些有用的值:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            # ... some options here ...
        },
    },
]

由于绝大多数引擎都是从文件加载模板的,所以每种模板引擎都包含两项通用设置:

  • DIRS 定义了一个目录列表,模板引擎按列表顺序搜索这些目录以查找模板源文件。
  • APP_DIRS 告诉模板引擎是否应该进入每个已安装的应用中查找模板。每种模板引擎后端都定义了一个惯用的名称作为应用内部存放模板的子目录名称。(译者注:例如django为它自己的模板引擎指定的是 ‘templates’ ,为jinja2指定的名字是‘jinja2’)

用法

django.template.loader 定义了两个函数以加载模板。

get_template(template_name[, dirs][, using])

该函数使用给定的名称加载模板并返回一个Template 对象.

get_template()select_template() 返回的Template 对象必须要有一个render()方法,协议如下:

Template.render(context=None, request=None)
通过给定的 context 对该模板进行渲染。

关于搜索算法的例子。该例子下 TEMPLATES 的配置是:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            '/home/html/example.com',
            '/home/html/default',
        ],
    },
    {
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        'DIRS': [
            '/home/html/jinja2',
        ],
    },
]

另外,为了减少加载模板、渲染模板等重复工作,django提供了处理这些工作的快捷函数。
render_to_string(template_name[, context][, context_instance][, request][, using])
render_to_string() 会像 get_template()一样加载模板并立即调用 render() 方法。 它需要以下参数:

  • template_name,需要加载并渲染的模板。如果它是一个列表,则Django会使用select_template()来寻找模板位置。
  • context,要用作模板的上下文进行渲染的dict。

内置后端

设置BACKEND 为 ‘django.template.backends.django.DjangoTemplates’ 来配置Django模板引擎。
当 APP_DIRS 为 True 时, DjangoTemplates 引擎会在已安装应用的 templates 子目录中查找模板文件。 这个通用名称是保持向后兼容的。
DjangoTemplates 引擎 OPTIONS 配置项中接受以下参数:

  • ‘allowed_include_roots’: 这是一个字符串列表,表示这些字符串允许出现在{% ssi %}中,作为被允许使用的模板标签。这是一个安全的措施,使模板作者不能访问他们不应该访问的文件。例如, 如果 ‘allowed_include_roots’ 是 [‘/home/html’, ‘/var/www’],那么 {% ssi /home/html/foo.txt %} 就会生效, 但是 {% ssi /etc/passwd %}就无效。它默认是个空的 list.
  • ‘context_processors’: 是一个包含以”.”为分隔符的python调用路径的列表,在一个template被request渲染时,它可以被调用以产生context的数据。这些可调用要求一个请求对象作为其参数,并返回要合并到上下文中的项目的dict。它默认为空列表。
  • ‘debug’:打开/关闭模板调试模式的布尔值。如果它True,那么奇怪的错误页面将显示模板渲染期间引发的任何异常的详细报告。此报告包含模板的相关代码段,并突出显示相应的行。它默认和setting中的 DEBUG有相同的值。
  • ‘loaders’:模板加载器类的虚拟Python路径列表。每个Loader类知道如何从特定源导入模板。你可以选择使用字符串元组来代替字符串。元组的第一项是 Loader类名,接下来的项在初始化期间会被传递给Loader。默认值取决于DIRS和APP_DIRS的值。
  • ‘string_if_invalid’:作为字符串的输出,模板系统应该用于无效(例如拼写错误的)变量。
  • ‘file_charset’:用于读取磁盘上的模板文件的字符集。

Django模板语言

如果您有过编程背景,或者您使用过一些在HTML中直接混入程序代码的语言,那么现在您需要记住,Django的模版系统并不是简单的将Python嵌入到HTML中。 设计决定了:模版系统致力于表达外观,而不是程序逻辑。

模版是纯文本文件。它可以产生任何基于文本的的格式(HTML,XML,CSV等等)。
模版包括在使用时会被值替换掉的 变量,和控制模版逻辑的 标签。
下面是一个小模版,它说明了一些基本的元素。后面的文档中会解释每个元素。

{% extends "base_generic.html" %}

{% block title %}{{ section.title }}{% endblock %}

{% block content %}
<h1>{{ section.title }}h1>

{% for story in story_list %}
<h2>
  <a href="{{ story.get_absolute_url }}">
    {{ story.headline|upper }}
  a>
h2>
<p>{{ story.tease|truncatewords:"100" }}p>
{% endfor %}
{% endblock %}

为什么要使用基于文本的模版,而不是基于XML的(比如Zope的TAL)呢?我们希望Django的模版语言可以用在更多的地方,而不仅仅是XML/HTML模版。在线上世界,我们在email、Javascript和CSV中使用它。你可以在任何基于文本的格式中使用这个模版语言。

变量

变量看起来就像是这样: {{ variable }}。

点号(.)用来访问变量的属性。从技术上来说,当模版系统遇到点(“.”),它将以这样的顺序查询:

  • 字典查询(Dictionary lookup)
  • 属性或方法查询(Attribute or method lookup)
  • 数字索引查询(Numeric index lookup)

过滤器

过滤器看起来是这样的:{{ name|lower }}。这将在变量 {{ name }} 被过滤器 lower 过滤后再显示它的值,该过滤器将文本转换成小写。使用管道符号 (|)来应用过滤器。
过滤器参数包含空格的话,必须被引号包起来;例如,使用逗号和空格去连接一个列表中的元素,你需要使用 {{ list|join:”, ” }}。

常用的模版过滤器:

  • default,如果一个变量是false或者为空,使用给定的默认值。否则,使用变量的值。例如:{{ value|default:"nothing" }}
  • length,返回值的长度。它对字符串和列表都起作用。例如:{{ value|length }}
  • filesizeformat,将该数值格式化为一个 “人类可读的” 文件容量大小 (例如 ‘13 KB’, ‘4.1 MB’, ‘102 bytes’, 等等)。例如:{{ value|filesizeformat }}

标签

标签看起来像是这样的: {% tag %}。标签比变量复杂得多:有些用于在输出中创建文本,有些用于控制循环或逻辑,有些用于加载外部信息到模板中供以后的变量使用。
有些标签需要开始标签和结束标签(例如{% tag %} … tag contents … {% endtag %})。
常用的标签:

  • for
  • if,elif,else
  • block和extend

注释

要注释模版中一行的部分内容,使用注释语法 {# #}.
例如,这个模版将被渲染为 ‘hello’:{# greeting #}hello

模版继承

Django模版引擎中最强大也是最复杂的部分就是模版继承了。模版继承可以让您创建一个基本的“骨架”模版,它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks 。
您可以根据需要使用多级继承。使用继承的一个常用方式是类似下面的三级结构:

  • 创建一个 base.html 模版来控制您整个站点的主要视觉和体验。
  • 为您的站点的每一个“分支”创建一个base_SECTIONNAME.html 模版。例如, base_news.html, base_sports.html。这些模版都继承自 base.html ,并且包含了每部分特有的样式和设计。
  • 为每一种页面类型创建独立的模版,例如新闻内容或者博客文章。这些模版继承对应分支的模版。

使用继承的一些提示:

  • 如果你在模版中使用 {% extends %} 标签,它必须是模版中的第一个标签。其他的任何情况下,模版继承都将无法工作。
  • 在base模版中设置越多的 {% block %} 标签越好。请记住,子模版不必定义全部父模版中的blocks,所以,你可以在大多数blocks中填充合理的默认内容,然后,只定义你需要的那一个。多一点钩子总比少一点好。
  • 如果你发现你自己在大量的模版中复制内容,那可能意味着你应该把内容移动到父模版中的一个 {% block %} 中。
  • 如果需要获取父模板中的block 的内容,可以使用{{ block.super }} 变量。如果你想要在父block 中新增内容而不是完全覆盖它,它将非常有用。使用{{ block.super }} 插入的数据不会被自动转义(参见下一节),因为父模板中的内容已经被转义。
  • 为了更好的可读性,你也可以给你的 {% endblock %} 标签一个 名字 。

自动HTML转义(XSS)攻击)

当从模版中生成HTML时,总会有这样一个风险:值可能会包含影响HTML最终呈现的字符。显然,用户提交的数据都被不应该被盲目的信任,并且被直接插入到你的网页中,因为一个怀有恶意的用户可能会使用这样的漏洞来做一些可能的坏事。这种类型的安全问题被叫做 跨站脚本(Cross Site Scripting) (XSS) 攻击。

默认情况下,Django 中的每个模板会自动转义每个变量的输出。明确地说,下面五个字符被转义:

  • < 会转换为<
  • > 会转换为>
  • ‘(单引号) 会转换为'
  • ” (双引号)会转换为 "
  • & 会转换为 &

如何关闭它

然而你为什么想要关闭它呢?由于有时,模板变量含有一些你打算渲染成原始HTML的数据,你并不想转义这些内容。例如,你可能会在数据库中储存一些HTML代码,并且直接在模板中嵌入它们。或者,你可能使用Django的模板系统来生成不是HTML的文本 – 比如邮件信息。

提供的几种方法:

  • 用于独立变量,使用safe过滤器来关闭独立变量上的自动转义:his will not be escaped: {{ data|safe }}
  • 用于模板代码块,要控制模板上的自动转义,将模板(或者模板中的特定区域)包裹在autoescape标签 中,像这样:{% autoescape off %}Hello {{ name }}{% endautoescape %}

字符串字面值和自动转义

访问方法调用

大多数对象上的方法调用同样可用于模板中。这意味着模板能够访问到的不仅仅是类属性(比如字段名称)和视图中传入的变量。例如,Django ORM提供了“entry_set” 语法用于查找关联到外键的对象集合。所以,如果模型“comment” 有一个外键关联到模型“task” ,你可以根据task 遍历其所有的comments,像这样:

{% for comment in task.comment_set.all %}
    {{ comment }}
{% endfor %}

自定义标签和过滤器库

某些应用提供自定义的标签和过滤器库。要在模板中访问它们,确保应用已经在INSTALLED_APPS 中(在这个例子中我们添加了’django.contrib.humanize’),之后在模板中使用load标签:

{% load humanize %}

{{ 45000|intcomma }}

自定义库和模板继承

当你加载一个自定义标签或过滤器库时,标签或过滤器只在当前模板中有效 – 并不是带有模板继承关系的任何父模板或者子模版中都有效。
例如,如果一个模板foo.html带有{% load humanize %},子模版(例如,带有{% extends “foo.html” %})中不能 访问humanize模板标签和过滤器。子模版需要添加自己的 {% load humanize %}。
这个特性是出于保持可维护性和逻辑性的目的。

你可能感兴趣的:(python,django)