Django第三篇-----模板前半篇

目录

什么是Django模板

模板标签详细说明

 Template 对象

错误标签

 渲染模板

字典和上下文

上下文变量查找

 方法调用的行为

 如何处理无效变量

基本的模板标签和过滤器

if/else

for

ifequal/ifnotequal

注释


什么是Django模板

Django 模板是一些文本字符串,作用是把文档的表现与数据区分开。模板定义一些占位符和基本的逻辑(模板标签),规定如何显示文档。通常,模板用于生成 HTML,不过 Django 模板可以生成任何基于文本的格式。

 Django 模板描述一个 HTML 页面

<html>
<head>
<title>Ordering notice</title>
</head>
<body>
25
<h1>Ordering notice</h1>
<p>Dear {{ person_name }},</p>
<p>Thanks for placing an order from {{ company }}. It's scheduled to ship on {{
ship_date|date:"F j, Y" }}.</p>
<p>Here are the items you've ordered:</p>
<ul>
{% for item in item_list %}
<li>{{ item }}</li>{% endfor %}
</ul>
{% if ordered_warranty %}
<p>Your warranty information will be included in the packaging.</p>
{% else %}
<p>
You didn't order a warranty, so you're on your own when
the products inevitably stop working.
</p>
{% endif %}
<p>Sincerely,<br />{{ company }}</p>
</body>
</html>

模板标签详细说明

• 两对花括号包围的文本(如 {{ person_name }} )是变量,意思是“把指定变量的值插入这里”。如何指
定变量的值呢?稍后说明。
• 一对花括号和百分号包围的文本(如 {% if ordered_warranty %} )是模板标签。
标签的定义相当宽泛:只要能让模板系统“做些事”的就是标签。
• 这个示例模板中有一个 for 标签( {% for item in item_list %} )和一个 if 标签( {% if or-
dered_warranty %} )。 for 标签的作用与 Python 中的 for 语句很像,用于迭代序列中的各个元素。与
你预期的一样, if 标签的作用是编写逻辑判断语句。

这里, if 标签检查 ordered_warranty 变量的求值结果是不是 True 。如果,模板系统将显示 {% if
ordered_warranty %} 和 {% else %}
之间的内容;如果不是,模板系统将显示 {% else %} 和 {% endif
%}
之间的内容。注意, {% else %} 是可选的
• 最后,这个模板的第二段包含一个过滤器,这是调整变量格式最为便利的方式。对这个示例中的 {{
ship_date|date:"F j, Y" }} 来说,我们把 ship_date 变量传给 date 过滤器,并且为 date 过滤器指定
"F j, Y" 参数
。 date 过滤器使用参数指定的格式格式化日期。过滤器使用管道符号( | )依附,类似
于 Unix 管道。

 Template 对象

若想在 Python 代码中使用 Django 的模板系统,基本方式如下:

1. 以字符串形式提供原始的模板代码,创建 Template 对象。
2. 在 Template 对象上调用 render() 方法,传入一系列变量(上下文)。返回的是完全渲染模板后得到的字符串,模板中的变量和模板标签已经根据上下文求出值了。

>>> from django import template
>>> t = template.Template('My name is {{ name }}.')
>>> c = template.Context({'name': 'Nige'})
>>> print (t.render(c))
My name is Nige.
>>> c = template.Context({'name': 'Barry'})
>>> print (t.render(c))
My name is Barry.

错误标签

>>> from django.template import Template #
>>> t = Template('{% notatag %}')
Traceback (most recent call last):
File "", line 1, in ?
...
django.template.base.TemplateSyntaxError: Invalid block tag: 'notatag'

这里的“block tag”(块级标签)指的是 {% notatag %} 。“块级标签”和“模板标签”是同一个事物。遇到下述各种情况时,模板系统都会抛出 TemplateSyntaxError :

• 无效标签
• 有效标签的无效参数
• 无效过滤器
• 有效过滤器的无效参数
• 无效模板句法
• 未关闭的标签(对需要结束标签的模板标签而言)

 渲染模板

有了 Template 对象之后,可以为其提供上下文,把数据传给它。上下文就是一系列模板变量和相应的值。模板使用上下文填充变量,求值标签。在 Django 中,上下文使用 django.template 模块中的 Context 类表示。它的构造方法接受一个可选参数:一个字典,把变量名映射到值上。“填充”模板的方式是,在 Template 对象上调用 render() 方法,并传入上下文:

>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'Stephane'})
>>> t.render(c)
'My name is Stephane.'

字典和上下文

得到 Template 对象之后,可以用其渲染多个上下文。例如:

>>> from django.template import Template, Context
>>> t = Template('Hello, {{ name }}')
>>> print (t.render(Context({'name': 'John'})))
Hello, John
>>> print (t.render(Context({'name': 'Julie'})))
Hello, Julie
>>> print (t.render(Context({'name': 'Pat'})))
Hello, Pat

像这样使用同一个模板渲染多个上下文,比分成多次创建 Template 对象再调用 render() 效率高:

# 不好
for name in ('John', 'Julie', 'Pat'):
t = Template('Hello, {{ name }}')
print (t.render(Context({'name': name})))
# 好
t = Template('Hello, {{ name }}')
for name in ('John', 'Julie', 'Pat'):
print (t.render(Context({'name': name})))

上下文变量查找

假如我们把一个 Python 字典传给模板。若想通过键访问那个字典中的值,要使用点号:


>>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name }} is {{person.age}} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
'Sally is 43 years old.'

类似地,通过点号还可以访问对象的属性。例如,Python 的 datetime.date 对象有 year 、 month 和 day 属性,在 Django 模板中可以使用点号访问这些属性

>>> from django.template import Template, Context
>>> import datetime
>>> d = datetime.date(1993, 5, 2)
>>> d.year
1993
>>> d.month
5
>>> d.day
2
>>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.')
>>> c = Context({'date': d})
>>> t.render(c)
'The month is 5 and the year is 1993.'

下述示例自定义一个类,以此说明也可以通过点号访问任意对象的属性

>>> from django.template import Template, Context
>>> class Person(object):
...     def __init__(self, first_name, last_name):
...         self.first_name, self.last_name =first_name, last_name
>>> t = Template('Hello, {{ person.first_name }} {{person.last_name }}.')
>>> c = Context({'person': Person('John', 'Smith')})
>>> t.render(c)
'Hello, John Smith.'

还可以通过点号引用对象的方法。例如,每个 Python 字符串都有 upper() 和 isdigit() 方法,在 Django 模板中可以使用点号句法调用它们

>>> from django.template import Template, Context
>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
>>> t.render(Context({'var': 'hello'}))
'hello -- HELLO -- False'
>>> t.render(Context({'var': '123'}))
'123 -- 123 -- True

注意,方法调用中没有括号。此外,不能给方法传递变量,只能调用无需参数的方法。(本章后面将说明这里的哲学。)最后,还可以使用点号访问列表索引,例如:

>>> from django.template import Template, Context
>>> t = Template('Item 2 is {{ items.2 }}.')
>>> c = Context({'items': ['apples', 'bananas','carrots']})
>>> t.render(c)
'Item 2 is carrots.'

不允许使用负数索引。例如,模板变量 {{ items.-1 }} 会导致 TemplateSyntaxError 抛出

总结起来,模板系统遇到变量名中的点号时会按照下述顺序尝试查找:

• 字典查找(如 foo["bar"] )
• 属性查找(如 foo.bar )
• 方法调用(如 foo.bar() )

• 列表索引查找(如 foo[2] )

 方法调用的行为

在方法查找的过程中,如果方法抛出异常,异常会向上冒泡,除非异常有 silent_variable_failure 属性,而且值为 True 。如果异常确实有 silent_variable_failure 属性,使用引擎的 string_if_invalid配置选项(默认为一个空字符串)渲染变量。例如:

>>> t = Template("My name is {{ person.first_name }}.")
>>> class PersonClass3:
...     def first_name(self):
...         raise AssertionError("foo")
>>> p = PersonClass3()
>>> t.render(Context({"person": p}))
Traceback (most recent call last):
...
AssertionError: foo
>>> class SilentAssertionError(Exception):
...     silent_variable_failure = True
>>> class PersonClass4:
...     def first_name(self):
...     raise SilentAssertionError
>>> p = PersonClass4()
>>> t.render(Context({"person": p}))
'My name is .'

• 方法不能有必须的参数。否则,模板系统向后移动到下一种查询类型(列表索引查询)。
• 按照设计,Django 限制了在模板中可以处理的逻辑量,因此在模板中不能给方法传递参数。数据应该在视图中计算之后再传给模板显示。
• 显然,有些方法有副作用,如果允许模板系统访问这样的方法,那就愚蠢之极,甚至还可能埋下安全漏洞。
• 假如有个 BankAccount 对象,它有个 delete() 方法。如果模板中有 {{ account.delete }} 这样的内容,其中 account 是 BankAccount 对象,那么渲染模板时会把 account 删除。

为了避免这种行为,在方
法上设定函数属性 alters_data :

def delete(self):
    # 删除账户
    delete.alters_data = True

这样标记之后,模板系统不会执行方法。继续使用前面的例子。如果模板中有 {{ account.delete}} ,而 delete() 方法设定了 alters_data=True ,那么渲染模板时不会执行 delete() 方法,引擎会使用string_if_invalid 的值替换那个变量。注意:为 Django 模型对象动态生成的 delete() 和 save() 方法自动设定了alters_data = True 。

 如何处理无效变量

一般来说,如果变量不存在,模板系统在变量处插入引擎的 string_if_invalid 配置选项。这个选项的默认值为一个空字符串。例如

>>> from django.template import Template, Context
>>> t = Template('Your name is {{ name }}.')
>>> t.render(Context())
'Your name is .'
>>> t.render(Context({'var': 'hello'}))
'Your name is .'
>>> t.render(Context({'NAME': 'hello'}))
'Your name is .'
>>> t.render(Context({'Name': 'hello'}))
'Your name is .'

基本的模板标签和过滤器

if/else

{% if today_is_weekend %}
<p>Welcome to the weekend!</p>
{% endif %}

{% else %} 标签是可选的

{% if today_is_weekend %}
<p>Welcome to the weekend!</p>
{% else %}
<p>Get back to work.</p>
{% endif %}

if 标签还可以有一个或多个 {% elif %} 子句:

{% if athlete_list %}
Number of athletes: {{ athlete_list|length }}
{% elif athlete_in_locker_room_list %}
<p>Athletes should be out of the locker room soon! </p>
{% elif ...
...
{% else %}
<p>No athletes. </p>
{% endif %}

{% if %} 支持使用 and 、 or 或 not 测试多个变量,或者取反指定的变量。例如:

{% if athlete_list and coach_list %}
<p>Both athletes and coaches are available. </p>
{% endif %}
{% if not athlete_list %}
<p>There are no athletes. </p>
{% endif %}
{% if athlete_list or coach_list %}
<p>There are some athletes or some coaches. </p>
{% endif %}
{% if not athlete_list or coach_list %}
<p>There are no athletes or there are some coaches. </p>
{% endif %}
{% if athlete_list and not coach_list %}
<p>There are some athletes and absolutely no coaches. </p>
{% endif %}

在同一个标签中可以同时使用 and 和 or ,此时, and 的优先级比 or 高。例如:

{% if athlete_list and coach_list or cheerleader_list %}

在 if 标签中使用括号是无效的句法。

多次使用相同的逻辑运算符没问题,但是不能混用不同的运算符。例如,下述写法是有效的:

{% if athlete_list or coach_list or parent_list or teacher_list %}

每个 {% if %} 都必须有配对的 {% endif %} 。否则,Django 会抛出 TemplateSyntaxError 异常。

for

{% for %} 标签用于迭代序列中的各个元素。与 Python 的 for 语句一样,句法是 for X in Y ,其中 Y 是要迭
代的序列, X 是单次循环中使用的变量。每次迭代时,模板系统会渲染 {% for %} 和 {% endfor %} 之间的内
容。例如,可以使用下述模板显示 athlete_list 变量中的运动员:

<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul

在标签中添加 reversed ,反向迭代列表

{% for %} 标签可以嵌套:

{% for athlete in athlete_list %}
<h1>{{ athlete.name }}</h1>
<ul>
    {% for sport in athlete.sports_played %}
    <li>{{ sport }}</li>
    {% endfor %}
</ul>
{% endfor %}

如果需要访问字典中的元素,也可以使用这个标签。如果上下文中包含一个字典 data ,可以使用下述模板显示字典的键和值

{% for key, value in data.items %}
{{ key }}: {{ value }}
{% endfor %}

这种做法太常见了,因此 for 标签支持一个可选的 {% empty %} 子句,用于定义列表为空时显示的内容。

{% for athlete in athlete_list %}
<p>{{ athlete.name }}</p>
{% empty %}
<p>There are no athletes. Only computer programmers.</p>
{% endfor %}

在循环结束之前,无法“跳出”。如果需要这么做,修改要迭代的变量,只包含需要迭代的值。同样,也没有“continue”语句,不能立即返回到循环的开头。

在 {% for %} 循环内部,可以访问一个名为 forloop 的模板变量。这个变量有几个属性,通过它们可以获知
循环进程的一些信息

• forloop.counter 的值是一个整数,表示循环的次数。这个属性的值从 1 开始,因此第一次循环时,
forloop.counter 等于 1 。下面举个例子:

{% for item in todo_list %}
<p>{{ forloop.counter }}: {{ item }}</p>
{% endfor %}

• forloop.counter0 与 forloop.counter 类似,不过是从零开始的。第一次循环时,其值为 0 。
• forloop.revcounter 的值是一个整数,表示循环中剩余的元素数量。第一次循环时, for-loop.revcounter 的值是序列中要遍历的元素总数。最后一次循环时, forloop.revcounter 的值为 1 。
• forloop.revcounter0 与 forloop.revcounter 类似,不过索引是基于零的。第一次循环时, for-loop.revcounter0 的值是序列中元素数量减去一。最后一次循环时, forloop.revcounter0 的值为 0 。
• forloop.first 是个布尔值,第一次循环时为 True 。需要特殊处理第一个元素时很方便:

{% for object in objects %}
    {% if forloop.first %}
        <li class="first">
    {% else %}
        <li>
    {% endif %}

    {{ object }}
    </li>
{% endfor %}

• forloop.last 是个布尔值,最后一次循环时为 True 。经常用它在一组链接之间放置管道符号:

{% for link in links %}
    {{ link }}{% if not forloop.last %} | {% endif %}
{% endfor %

• 在嵌套的循环中, forloop.parentloop 引用父级循环的 forloop 对象。下面举个例子:

{% for country in countries %}
    <table>
        {% for city in country.city_list %}
        <tr>
            <td>Country #{{forloop.parentloop.counter}}</td>
            <td>City #{{ forloop.counter }}</td>
            <td>{{ city }}</td>
        </tr>
        {% endfor %}
    </table>
{% endfor %}

forloop 变量只在循环内部可用。模板解析器遇到 {% endfor %} 时, forloop 随之消失。

ifequal/ifnotequal

Django 模板系统不是功能全面的编程语言,因此不允许执行任意的 Python 语句(3.5 节详述)。然而,模板经常需要比较两个值,在相等时显示一些内容。为此,Django 提供了 {% ifequal %} 标签。{% ifequal %} 标签比较两个值,如果相等,显示 {% ifequal %} 和 {% endifequal %} 之间的内容。下述示例比较模板标签 user 和 currentuser :

{% ifequal user currentuser %}
    <h1>Welcome!</h1>
{% endifequal %}

与 {% if %} 一样, {% ifequal %} 标签支持可选的 {% else %} 子句:

{% ifequal section 'sitenews' %}
    <h1>Site News</h1>
{% else %}
    <h1>No News Here</h1>
{% endifequal %}

{% ifequal %} 的参数只能是模板变量、字符串、整数和小数。下面是有效的示例:

{% ifequal variable 1 %}
{% ifequal variable 1.23 %}
{% ifequal variable 'foo' %}
{% ifequal variable "foo" %}

ifequal 标签可以替换成 if 标签和 == 运算符。
{% ifnotequal %} 的作用与 ifequal 类似,不过它测试两个参数是否不相等。 ifnotequal 标签可以替换成 if
标签和 != 运算符。

注释

与 HTML 和 Python 一样,Django 模板语言支持注释。注释使用 {# #} 标明:

{# This is a comment #}

渲染模板时,不输出注释。使用这种句法编写的注释不能分成多行。这一限制有助于提升模板解析性能。

{% comment %}
This is a
multi-line comment.
{% endcomment %}

猜你喜欢

转载自blog.csdn.net/Da___Vinci/article/details/84227782