django框架2——模板
直接将HTML硬编码到你的视图里却并不是一个好主意:
- 对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python
代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多 - Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人
员(甚至不同部门)来完成。 设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成
他们的工作。 - 程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另
一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。
模板系统基本知识
模板是一个文本,用于分离文档的表现形式和内容。 模板定义了占位符以及各种用于规范文档该如何显示的各部分基本逻辑(模板标签)。 模板通常用于产生HTML,但是Django的模板也能产生任何基于文本格式的文档。
让我们从一个简单的例子模板开始。 该模板描述了一个向某个与公司签单人员致谢 HTML 页面。 可将其视为
一个格式信函:
<html>
<head>
<title>Ordering notice</title>
</head>
<body>
<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>
{% endif %}
<p>Sincerely,<br/>{{ company }}</p>
</body>
</html>
变量、模板标签和filter
用两个大括号括起来的文字{{ person_name }} )称为变量(variable) 。此处插入指定变量的值。被大括号和百分号包围的文本(例如{% if ordered_warranty %} )是模板标签(template tag) 。标签(tag)
定义比较明确,即:仅通知模板系统完成某些工作的标签。
for标签类似Python的for语句,可让你循环访问序列里的每一个项目。if 标签是用来执行逻辑判断的。在这里,tag标签检查ordered_warranty值是否为True。如果是,模板系统将显示{% if ordered_warranty %}和{% else %}之间的内容;否则将显示{% else %}和{% endif %}之间的内容。{% else %}是
可选的。
最后,这个模板的第二段中有一个关于filter过滤器的例子,它是一种最便捷的转换变量输出格式的方式。如这个例子中的{{ship_date|date:”F j, Y” }},我们将变量ship_date传递给date过滤器,同时指定参数”Fj,Y”。date过滤器根据参数进行格式输出。过滤器是用管道符(|)来调用的
如何使用系统模板
模板系统是一个Python库,你可以在任何地方使用它,而不仅仅是在Django视图中。在Python代码中使用Django模板的最基本方式如下:
- 可以用原始的模板代码字符串创建一个 Template 对象, Django同样支持用指定模板文件路径的方式来创建 Template 对象;
- 调用模板对象的render方法,并且传入一套变量context。它将返回一个基于模板的展现字符串,模板中
的变量和标签会被context值替换。
D:\python\django_site\firstsite>python manage.py shell #启动交互解释器
>>> from django import template
>>> t = template.Template('My name is {{ name }}.')
>>> c = template.Context({'name': 'Adrian'})
>>> print(t.render(c))
My name is Adrian.
使用同一模板源渲染多个context,只进行 一次模板创建然后多次调用render()方法
渲染会更为高效
from django import template
t = template.Template('My name is {{ name }}.')
for name in ('John', 'Julie', 'Pat'):
c = template.Context({'name': name})
print(t.render(c))
深度遍历的查找
模板系统能够非常简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。
在 Django 模板中遍历复杂数据结构的关键是句点字符 (.)。
person = {'name': 'Sally', 'age': '43'}
t = template.Template('{{person.name}} is {{person.age} years old}.')
c = template.Context({'person':person})
t.render(c)
可以通过句点(.)来访问对象的属性。
>>> 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)
u'Hello, John Smith.'
还可以通过句点(.)来访问对象的方法。注意这里调用方法时并没有使用圆括号 而且也无法给该方法传递参数;只能调用不需参数的方法。
>>>from django import template
>>> t = template.Template('{{s.capitalize}}')
>>> c = template.Context({'s':'abc'})
>>> t.render(c)
'Abc'
句点也可用于访问列表索引;不允许使用负数列表索引。 像 {{ items.‐1 }} 这样的模板变量将会引发 TemplateSyntaxError
>>> from django.template import Template, Context
>>> t = Template('Item 2 is {{ items.2 }}.')
>>> c = Context({'items': ['apples', 'bananas', 'carrots']})
>>> t.render(c)
u'Item 2 is carrots.'
>>> t = Template('Item 2 is {{ items.pop }}.') #默认参数属于不需参数的一种情况
>>> c = Context({'items': ['apples', 'bananas', 'carrots']})
>>> t.render(c)
'Item 2 is carrots.'
当模板系统在变量名中遇到点时,按照以下顺序尝试进行查找:
- 字典类型查找 (比如 foo["bar"] )
- 属性查找 (比如 foo.bar )
- 方法调用 (比如 foo.bar() )
- 列表类型索引查找 (比如 foo[bar] )
>>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name.upper }} is {{ person.age }} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
u'SALLY is 43 years old.'
默认情况下,如果一个变量不存在,模板系统会把它展示为空字符串,不做任何事情来表示失败。
句点查找可以多级深度嵌套。
多数时间,你可以通过传递一个完全填充(full populated)的字典给 Context() 来初始化 上下文(Context) 。
但是初始化以后,你也可以使用标准的Python字典语法(syntax)向上下文(Context)
对象添加或者删除条目:
>>> from django.template import Context
>>> c = Context({"foo": "bar"})
>>> c['foo']
'bar'
>>> del c['foo']
>>> c['foo']
Traceback (most recent call last):
...
KeyError: 'foo'
>>> c['newvariable'] = 'hello'
>>> c['newvariable']
'hello'
模板——标签
for标签
遍历每一个元素:
{% for person in person_list %}
<p>{{ person.name }}</p>
{% endfor %}
可以利用{% for obj in list reversed %}
反向完成循环。
遍历一个字典:
{% for key,val in dic.items %}
<p>{{ key }}:{{ val }}</p>
{% endfor %}
注:循环序号可以通过{{forloop}}显示
forloop.counter The current iteration of the loop (1-indexed)
forloop.counter0 The current iteration of the loop (0-indexed)
forloop.revcounter The number of iterations from the end of the loop (1-indexed)
forloop.revcounter0 The number of iterations from the end of the loop (0-indexed)
forloop.first True if this is the first time through the loop
forloop.last True if this is the last time through the loop
为列表的每个单词的加上逗号。
Favorite places:
{% for p in places %}{{ p }}{% if not forloop.last %}, {% endif %}{% endfor %}
注释
就像HTML或者Python,Django模板语言同样提供代码注释。 注释使用 {# #} :
{# This is a comment #}
如果要实现多行注释,可以使用 {% comment %}
模板标签,就像这样:
{% comment %}
This is a
multi‐line comment.
{% endcomment %}
for ... empty标签
for
标签带有一个可选的{% empty %}
从句,以便在给出的组是空的或者没有被找到时,可以有所操作。
{% for person in person_list %}
<p>{{ person.name }}</p>
{% empty %}
<p>sorry,no person here</p>
{% endfor %}
if标签
{% if %}
会对一个变量求值,如果它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。
{% if num > 100 or num < 0 %}
<p>无效</p>
{% elif num > 80 and num < 100 %}
<p>优秀</p>
{% else %}
<p>其他</p>
{% endif %}
{% if %} 标签不允许在同一个标签中同时使用 and 和 or ,因为逻辑上可能模糊的,例如,如下示例是错误
的: 比如这样的代码是不合法的:
{% if athlete_list and coach_list or cheerleader_list %}
系统不支持用圆括号来组合比较操作。 如果你确实需要用到圆括号来组合表达你的逻辑式,考虑将它移到模板之外处理,然后以模板变量的形式传入结果吧。 或者,仅仅用嵌套的{% if %}标签替换吧,就像这样
{% if athlete_list %}
{% if coach_list or cheerleader_list %}
We have athletes, and either coaches or cheerleaders!
{% endif %}
{% endif %}
多次使用同一个逻辑操作符是没有问题的,但是我们不能把不同的操作符组合起来。 例如,这是合法的:
{% if athlete_list or coach_list or parent_list or teacher_list %
with
给一个复杂的变量命名
{% with total=business.employees.count %}
{{ total }} employee{{ total|pluralize }}
{% endwith %}
csrf_token
这个标签用于跨站请求伪造保护
<form action="/login/" method="post">
{% csrf_token %}
用户名 <input type="text" name="user">
密码 <input type="password" name="pwd">
<input type="submit">
</form>
setting.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', #改功能在此处关闭
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
过滤器
1.default
如果一个变量是false或者为空,使用给定的默认值。否则,使用变量的值。例如
{{ value|default:"nothing" }}
2.length
返回值的长度。它对字符串和列表都起作用。例如:
{{ value|length }}
如果 value 是 ['a', 'b', 'c', 'd'],那么输出是 4。
3.filesizeformat
将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB'
, '4.1 MB'
, '102 bytes'
, 等等)。例如:
{{ value|filesizeformat }}
如果 value
是 123456789,输出将会是 117.7 MB
。
4.data
如果 value=datetime.datetime.now()
{{ value|date:"Y-m-d"}}
5.slice
如果 value="hello world"
{{ value|slice:"2:-1"}}
6.truncatechars
如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。
例如:
{{ value|truncatechars:9}}
7.safe
Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。比如:
value="<a href="">点击</a>"
模板加载
为了减少模板加载调用过程及模板本身的冗余代码,Django 提供了一种使用方便且功能强大的 API ,用于从
磁盘中加载模板,要使用此模板加载API,首先你必须将模板的保存位置告诉框架。
打开你的settings.py配置文件,找到TEMPLATE_DIRS这项设置。
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(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',
],
},
},
]
views.py
from django.shortcuts import render
from datetime import datetime
def change(request):
da = datetime.datetime(2019,10,12)
t = da.today
return render(request,'change.html',{'today':t,'item':['a','b','c'],'num':89,})
不再需要导入 get_template 、 Template 、 Context 和 HttpResponse 。相反,我们导入
django.shortcuts.render 。 模板加载、上下文创建、模板解析和HttpResponse 创建工作均在对 render() 的调用中完成了。 由于 rendere() 返回 HttpResponse 对象,因此我们仅需在视图中 return 该值。render() 的第二个参数必须是要使用的模板名称。 第三个参数是为该模板创建 Context 时所使用的字典。
locals()
传入了 locals() 的值,它囊括了函数执行到该时间点时所定义的一切变量。
return render(request,'change.html',locals())
include 模板标签
nav.html
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
<li>e</li>
<li>f</li>
<li>g</li>
</ul>
base.html中适当位置写入下面代码即可
{% include 'nav.html' %}
模板继承
将可能要更改的地方放入
母板base.html
<div class="jumbotron">
{% block main_content %}
<h1>Hello, world!</h1>
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
{% endblock main_content %}
子模板child.html
{% extends "base.html" %}
{% block main_content %}
{{block.super}} #保留父类
<h4>Adobe全家桶</h4>
<h4>Adobe Acrobat DC</h4>
<h4>Adobe premiere</h4>
{% endblock main_content %}
模板继承类比于类的继承,父类中可以包含许多block,{% extends "base.html" %}:继承父类,{% block blockname %} :重写父类方法, {{block.super}} :保留父类方法并增加功能
根据需要使用任意多的继承次数。 使用继承的一种常见方式是下面的三层法:
创建 base.html 模板,在其中定义站点的主要外观感受。 这些都是不常修改甚至从不修改的部分。
为网站的每个区域创建 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。这些模
板对 base.html 进行拓展,并包含区域特定的风格与设计。-
为每种类型的页面创建独立的模板,例如论坛页面或者图片库。 这些模板拓展相应的区域模板。
这个方法可最大限度地重用代码,并使得向公共区域(如区域级的导航)添加内容成为一件轻松的工作。
以下是使用模板继承的一些诀窍:
如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作
用。
一般来说,基础模板中的 {% block %} 标签越多越好。 记住,子模板不必定义父模板中所有的代码块,因
此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。 俗话
说,钩子越多越好。 如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。
如果你需要访问父模板中的块的内容,使用 {{ block.super }}这个标签,这一个魔法变量将会表现出
父模板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。
不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双
向的。 也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模
板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。
{% extends %} 对所传入模板名称使用的加载方法和 get_template() 相同。 也就是说,会将模板名称被添
加到 TEMPLATE_DIRS 设置之后。
多数情况下, {% extends %} 的参数应该是字符串,但是如果直到运行时方能确定父模板名,这个参数也可以是个变量。