HTML模板简介
引言:{{ }}和 {% %},变量相关的用{{}},逻辑相关的用{%%}。
变量:
用两个大括号括起来的文字(例如 {{ person_name }} )称为 变量(variable)。
模板标签:
被大括号和百分号包围的文本(例如 {% if ordered_warranty %} )是 模板标签(template tag) ,即通知模板系统完成某些工作的标签(比如if,for)。
过滤器:
通过管道符(|)来调用过滤器:
{{ship_date|date:”F j, Y” }};#将变量ship_date传递给date过滤器,同时指定参数”F j,Y”
使用模板的基本方式
- 使用 django.template 模块中的Template 类实例化Template 对象,构造函数接受一个参数,该参数可以是原始的模板代码字符串,也可以是模板文件路径;
- 调用模板对象的render方法,并且传入一套变量context来渲染模板。他将返回一个模板中的变量和标签被context值替换后的字符串。
Django模板系统的基本规则
写模板,创建 Template 对象,创建 Context , 调用 render() 方法。
Template对象的创建
- 转到project目录(由 django-admin.py startproject 命令创建),输入命令 python manage.py shell 启动交互界面,(相较于直接使用python命令,它增加了在配置文件.bash_profile中自动设置DJANGO_SETTINGS_MODULE环境变量的功能,告诉Django使用哪个设置文件(即setting.py))
- 创建Template文件
from django.template import Template
t = Template('My name is {{ name }}.')
一旦你创建一个 Template 对象,你可以用 context 来传递数据给它,context在Django里表现为 Context 类,在 django.template 模块里。它的构造函数带有一个可选的参数: 字典-映射变量和它们的值。 最后调用 Template 对象 的 render() 方法并传递context来填充模板:
Context对象的数据传递:
1. 用字典传递数据给Context
from django.template import Context, Template
t = Template('My name is {{ name }}.')
c = Context({'name': 'Stephane'})
t.render(c)
或者多个元素的字典:
from django.template import Template, Context
normal = {'name': 'Sally', 'age': '43'}
t = Template('{{ person.name }} is {{ person.age }} years old.')
c = Context({'person': nomal)
t.render(c)
2. 用类实例化对象传递数据给Context
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)
当数据有自带的方法时,也可以在Template实例化时直接调用,但是注意不可以带括号:
from django.template import Template, Context
t1 = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
t1.render(Context({'var': 'hello'}))#当调用字符串本身的方法时,比如str.upper(),此时方法的括号一定要去掉!!!
3. 用列表传递数据给Context
from django.template import Template, Context
t = Template('Item 2 is {{ items.2 }}.')
c = Context({'items': ['apples', 'bananas', 'carrots']})#context为列表,可以访问列表索引
t.render(c)
注意事项:
当模板系统在变量名中遇到a.b形式传递数据时,按照以下顺序尝试进行查找:
-
字典类型查找 (比如 foo[“bar”] )
-
属性查找 (比如 foo.bar )
-
方法调用 (比如 foo.bar() )
-
列表类型索引查找 (比如 foo[bar] )
查找时遵循短路逻辑:系统使用找到的第一个有效类型。 且a.b形式的句点查找可以多级深度嵌套。 例如 {{person.name.upper}} 会转换成字典类型查找( person[‘name’] ) 然后再进行方法调用( upper() )。
4. Context对象的增添
Context对象可以使用c[‘name’] = 'guanguan’这样的形式来添加条目。
基本的模板标签和过滤器
标签
- if/else
{% if %} 标签检查一个变量,如果这个变量为真,系统会显示在 {% if %} 和 {% endif %} 之间的任何内容,注意{% else %} 标签是可选的:
{% if today_is_weekend %}
<p>Welcome to the weekend!</p>
{% else %}
<p>Get back to work.</p>
{% endif %}
{% if %} 标签接受 and,or 或者 not 关键字来对多个变量做判断 ,但不允许在同一个标签中同时使用 and 和 or ,且不支持用圆括号来组合比较操作,也没有 {% elif %} 标签。
{% if athlete_list and coach_list %}
Both athletes and coaches are available.
{% endif %}
- for
{% for %} 允许我们在一个序列上迭代。循环语法是 for X in Y
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
列表反向迭代:
{% for athlete in athlete_list reversed %}
...
{% endfor %}
{% for %} 标签可以嵌套,且支持一个可选的{% empty %}
分句,通过它来定义循环的列表是否为空:
{% for athlete in athlete_list %}
<p>{{ athlete.name }}</p>
{% empty %}
<p>There are no athletes. Only computer programmers.</p>
{% endfor %}
该标签不支持continue和break语句;但每个{% for %}
循环里有一个仅仅能够在循环中使用的模板变量forloop
,用来表示当前循环的执行次数。第一次循环时 forloop.counter 将会被设置为1。
{% for item in todo_list %}
<p>{{ forloop.counter }}: {{ item }}</p>
{% endfor %}
forloop.counter0
: 类似于 forloop.counter ,但从0开始计数;
forloop.revcounter
: 表示循环中剩余项的整型变量。 在循环初次执行时被设置为序列中项的总数。 最后一次循环执行中,这个变量将被置1;
forloop.revcounter0
: 类似于 forloop.revcounter ,但它以0做为结束索引。 在第一次执行循环时,该变量会被置为序列的项的个数减1;
forloop.first
:布尔值,如果该迭代是第一次执行,那么它被置为true;
forloop.last
: 布尔值;在最后一次执行循环时被置为True;
forloop.parentloop
: 指向当前循环的上一级循环的 forloop 对象的引用。
- ifequal/ifnotequal
{% ifequal %} 标签比较两个值,当他们相等时,显示在 {% ifequal %} 和 {% endifequal %} 之中所有的值。
注意:只有模板变量,字符串,整数和小数可以作为 {% ifequal %} 标签的参数
{% ifequal section 'sitenews' %}
<h1>Site News</h1>
{% else %}
<h1>No News Here</h1>
{% endifequal %}
注释
注释使用 {# #} :注释不能跨越多行
如果要实现多行注释,可以使用{% comment %}
模板标签
{% comment %}
This is a
multi-line comment.
{% endcomment %}
过滤器
模板过滤器是在变量被显示前修改它的值的一个简单方法
{{ name|lower }}
显示的内容是变量 {{ name }} 被过滤器 lower 处理后的结果,它功能是转换文本为小写。
过滤管道可以被套接,既一个过滤器管道的输出又可以作为下一个管道的输入:
{{ my_list|first|upper }}#查找列表的第一个元素并将其转化为大写
有些过滤器有参数。 过滤器的参数跟随冒号之后并且以双引号包含:
{{ bio|truncatewords:"30" }}
一些重要的过滤器:
- addslashes : 添加反斜杠到任何反斜杠、单引号或者双引号前面;
- date : 按指定的格式字符串参数格式化 date 或者 datetime 对象;
- length : 返回变量的长度。 对于列表,这个参数将返回列表元素的个数。 对于字符串,这个参数将返回字符串中字符的个数。
提升:从磁盘中加载模板
TEMPLATE设置
Django 提供了一种使用方便且功能强大的 API ,用于从磁盘中加载模板,为了使用该模板加载API,必须将模板的保存位置告诉框架。
首先在项目目录下新建目录templates,用来存放模板。然后打开settings.py配置文件,找到TEMPLATES这项设置,将templates目录的路径添加到 TEMPLATES 的DIRS属性中:
TEMPLATES = [
{...
‘DIRS’ = ['/home/django/mysite/templates'],//不加方括号会产生找不到templates文件的错误
...
}
]
注意:Python 要求单元素元组中必须使用逗号,以此消除与圆括号表达式之间的歧义
修改视图代码
修改代码使其可以一次性地载入某个模板文件,渲染它,然后将此作为 HttpResponse返回。
from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
t = get_template('temp/current_datetime.html')# get_template()方法会自动为你连接已经设置的 TEMPLATES目录和你传入该方法的模板名称参数,temp为子目录。
html = t.render({'current_date': now})#字典前面没有加Context,因为加了Context之后在我电脑上会发生错误
return HttpResponse(html)
上述代码可以简化为:
from django.shortcuts import render_to_response
import datetime
def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('temp/current_datetime.html', {'current_date': now})
内建函数locals()缩减代码量的使用技巧:
def current_datetime(request):
current_date = datetime.datetime.now()#相比于之前将获得的日期赋予变量now再使用字典将now映射到模板需要的变量名称current_data,我们直接用current_data获取日期(即使用与模板变量相同的名称),再使用locals()来返回一个**所有**局部变量的名称与值映射的字典,代码量大大减少。
return render_to_response('current_datetime.html', locals())
include模板标签与继承机制
include模板标签
该标签允许在(模板中)包含其它的模板的内容。 标签的参数是所要包含的模板名称,可以是一个变量,也可以是用单/双引号硬编码的字符串。
{% include 'includes/nav.html' %}
{% include template_name %}
模板继承
模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。
模板标签{% block %}
每个{% block %}标签所要做的是告诉模板引擎,该模板下的这一块内容将有可能被子模板覆盖。
- 创建一个基础模板base.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<h1>My helpful timestamp site</h1>
{% block content %}{% endblock %}
{% block footer %}
<hr>
<p>Thanks for visiting my site.</p>
{% endblock %}
</body>
</html>
- 在子模版current_time.html中使用它:
由于{% extends %} 对所传入模板名称使用的加载方法和 get_template() 相同。 也就是说,会将模板名称添加到 TEMPLATES的DIRS 设置之后。而我们在设置时并未指定子目录,所以在这里需要加上。
{% extends "./base.html" %}#代表当前temp1目录下的base.html文件(不指定路径是会产生无法找到template的错误)
{% block title %}The current time{% endblock %}
{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}
继承的三层法:
-
创建 base.html 模板,在其中定义站点的主要外观感受。 这些都是不常修改甚至从不修改的部分。
-
为网站的每个区域创建 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。这些模板对 base.html 进行拓展,并包含区域特定的风格与设计。2
-
为每种类型的页面创建独立的模板,例如论坛页面或者图片库。 这些模板拓展相应的区域模板。
注意:
-
必须保证{% extends %} 为模板中的第一个模板标记。 否则,模板继承将不起作用。
-
一般来说,基础模板中的 {% block %} 标签(钩子)越多越好。 但是子模板不必定义父模板中所有的代码块,因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。
-
如果发觉自己在多个模板之间拷贝代码,应该考虑将该代码段放置到父模板的某个 {% block %} 中。
-
如果需要访问父模板中的块的内容,使用 {{ block.super }}标签,这一个魔法变量将会表现出父模板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。
-
不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。 也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。