这会导致几个问题:
1,显然,任何页面的改动会牵扯到Python代码的改动
网站的设计改动会比Python代码改动更频繁,所以如果我们将两者分离开会更方便
2,其次,写后台Python代码与设计HTML是不同的工作,更专业的Web开发应该将两者分开
页面设计者和HTML/CSS程序员不应该编辑Python代码,他们应该与HTML打交道
3,程序员写Python代码同时页面设计者写HTML模板会更高效,而不是一个人等待另一个人编辑同样的文件
因此,使用Django的模板系统分离设计和Python代码会更干净更易维护
模板系统基础
Django模板是一个string文本,它用来分离一个文档的展现和数据
模板定义了placeholder和表示多种逻辑的tags来规定文档如何展现
通常模板用来输出HTML,但是Django模板也能生成其它基于文本的形式
让我们来看看一个简单的模板例子:
- <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>
这个模板本质上是HTML,但是夹杂了一些变量和模板标签:
1,用{{}}包围的是变量,如{{person_name}},这表示把给定变量的值插入,如何指定这些变量的值我们即将说明
2,用{%%}包围的是块标签,如{%if ordered_warranty%}
块标签的含义很丰富,它告诉模板系统做一些事情
在这个例子模板中包含两个块标签:for标签表现为一个简单的循环结构,让你按顺序遍历每条数据
if标签则表现为逻辑的if语句
在这里,上面的标签检查ordered_warranty变量的值是否为True
如果是True,模板系统会显示{%if ordered_warranty%}和{%endif%}之间的内容
否则,模板系统不会显示这些内容
模板系统也支持{%else%}等其它逻辑语句
3,上面还有一个过滤器的例子,过滤器是改变变量显示的方式
上面的例子中{{ship_date|date:"F j, Y"}}把ship_date变量传递给过滤器
并给date过滤器传递了一个参数“F j, Y”,date过滤器以给定参数的形式格式化日期
类似于Unix,过滤器使用管道字符“|”
Django模板支持多种内建的块标签,并且你可以写你自己的标签
使用模板系统
在Python代码中使用模板系统,请按照下面的步骤:
1,用模板代码创建一个Template对象
Django也提供指定模板文件路径的方式创建Template对象
2,使用一些给定变量context调用Template对象的render()方法
这将返回一个完全渲染的模板,它是一个string,其中所有的变量和块标签都会根据context得到值
创建模板对象
最简单的方式是直接初始化它,Template类在django.template模块中,初始化方法需要一个参数
下面进入Python交互环境看看:
- >>> from django.template import Template
- >>> t = Template("My name is {{my_name}}.")
- >>> print t
你将看到如下信息
- <django.template.Template object at 0xb7d5f24c>
0xb7d5f24c每次都会改变,但是无所谓,它是Template对象的Python“identity”
在这本书中,我们会在Python的交互式会话环境中展示一些示例。当你看到三个大于号'>>>',就可以确定是在交互环境中了。
如果你从本书中拷贝代码,记得不要拷贝这些大于号。
当你创建Template对象,模板系统会编译模板代码,并准备渲染
如果你的模板代码有语法错误,调用Template()方法会触发TemplateSyntaxError异常
- >>> from django.template import Template
- >>> t = Template('{%notatag%}')
- Traceback (most recent call last):
- File "<stdin>", line 1, in ?
- ...
- django.template.TemplateSyntaxError: Invalid block tag: 'notatag'
系统触发TemplateSyntaxError异常可能出于以下情况:
1,不合法的块标签
2,合法块标签接受不合法的参数
3,不合法的过滤器
4,合法过滤器接受不合法的参数
5,不合法的模板语法
6,块标签没关
渲染模板
一旦你拥有一个Template对象,你可以通过给一个context来给它传递数据
context是一个变量及赋予的值的集合,模板使用它来得到变量的值,或者对于块标签求值
这个context由django.template模块的Context类表示
它的初始化函数有一个可选的参数:一个映射变量名和变量值的字典
通过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.'
变量名必须以字母(A-Z或a-z)开始,可以包含数字,下划线和小数点,变量名大小写敏感
下面是一个模板编译和渲染的例子,使用这章开始时的模板例子:
- >>> from django.template import Template, Context
- >>> raw_template = """<p>Dear {{ person_name }},</p>
- ...
- ... <p>Thanks for ordering {{ product }} from {{ company }}. It's scheduled to
- ... ship on {{ ship_date|date:"F j, Y" }}.</p>
- ...
- ... {% if ordered_warranty %}
- ... <p>Your warranty information will be included in the packaging.</p>
- ... {% endif %}
- ...
- ... <p>Sincerely,<br />{{ company }}</p>"""
- >>> t = Template(raw_template)
- >>> import datetime
- >>> c = Context({'person_name': 'John Smith',
- ... 'product': 'Super Lawn Mower',
- ... 'company': 'Outdoor Equipment',
- ... 'ship_date': datetime.date(2009, 4, 2),
- ... 'ordered_warranty': True})
- >>> t.render(c)
- "<p>Dear John Smith,</p>\n\n<p>Thanks for ordering Super Lawn Mower from Outdoor Equipment.
- It's scheduled to ship on April 2, 2009.</p>\n\n<p>Your warranty information will be included
- in the packaging.</p>\n\n\n<p>Sincerely,<br />Outdoor Equipment</p>"
让我们来看看都做了些什么:
1,我们import Template和Context类,它们都在django.template模块里面
2,我们把模板文本存储在raw_template变量里,我们使用"""来构建string,它可以跨越多行
3,我们创建模板对象t,并给Template类的初始化函数传递raw_template变量
4,我们从Python的标准库import datetime模块,下面会用到它
5,我们创建一个context对象c,它的初始化函数使用一个映射变量名和变量值的字典
例如我们指定person_name的值为'John Smith',product的值为'Super Lawn Mower'等等
6,最后,我们调用模板对象的render()方法,参数为context对象c
这将返回渲染后的模板,将模板中的变量值替换并计算块标签的结果
如果你刚接触Python,你可能会问为什么输出中包含了新行字符'\n'而不是换行
这是因为Python交互环境中调用t.render(c)会显示string的representation而不是string的值
如果你想看到换行而不是'\n',使用print t.render(c)即可
上面是使用Django模板系统的基础,只是创建一个模板对象和context对象然后调用render()方法
同一个模板,多个context的情况:
一旦你创建了一个模板对象,你可以渲染多个context,例如:
- >>> 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
无论何时,你使用同一个模板来渲染多个context的话,创建一次Template对象然后调用render()多次会更高效
- # Bad
- for name in ('John', 'Julie', 'Pat'):
- t = Template('Hello, {{ name }}')
- print t.render(Context({'name': name}))
- # Good
- t = Template('Hello, {{ name }}')
- for name in ('John', 'Julie', 'Pat'):
- print t.render(Context({'name': name}))
Django的模板解析非常快,在后台,大部分的解析通过一个单独的对正则表达式的调用来做
这与基于XML的模板引擎形成鲜明对比,XML解析器比Django的模板渲染系统慢很多
Context变量查找
上面的例子中,我们给模板context传递了简单的值,大部分是string,以及一个datetime.date
尽管如此,模板系统优雅的处理更复杂的数据结构,如列表,字典和自定义对象
在Django模板系统中处理复杂数据结构的关键是使用(.)字符
使用小数点来得到字典的key,属性,对象的索引和方法
下面通过例子来解释,通过(.)访问字典的key:
- >>> 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.'
通过(.)来访问对象的属性:
- >>> 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.'
小数点也可以用来调用列表的索引:
- >>> from django.template import Template, Context
- >>> t = Template('Item 2 is {{ items.2 }}.')
- >>> c = Contexst({'items': ['apples', 'bananas', 'carrots']})
- >>> t.render(c)
- 'Item 2 is carrots.'
负数的列表索引是不允许的,例如模板变量{{ items.-1 }}将触发TemplateSyntaxError
最后小数点还可以用来访问对象的方法,例如Python的string有upper()和isdigit()方法:
- >>> 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'
注意,调用方法时你不能加括号,你也不能给方法传递参数
你只能调用没有参数的方法,后面我们会解释这些
总结一下,当模板系统遇到变量名里有小数点时会按以下顺序查找:
1,字典查找,如foo["bar"]
2,属性查找,如foo.bar
3,方法调用,如foo.bar()
3,列表的索引查找,如foo[bar]
小数点可以多级纵深查询,例如{{ person.name.upper }}表示字典查询person['name']然后调用upper()方法
- >>> 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)
- 'SALLY is 43 years old.'
关于方法调用
方法调用要比其他的查询稍微复杂一点,下面是需要记住的几点:
1,在方法查询的时候,如果一个方法触发了异常,这个异常会传递从而导致渲染失
败,但是如果异常有一个值为True的silent_variable_failure属性,这个变量会渲染成空string:
- >>> t = Template("My name is {{ person.first_name }}.")
- >>> class PersonClas3:
- ... def first_name(self):
- ... raise AssertionError, "foo"
- >>> p = PersonClass3()
- >>> t.render(Context({"person": p}))
- Traceback (most recent call last):
- ...
- AssertionError: foo
- >>> class SilentAssetionError(AssertionError):
- ... silent_variable_failure = True
- >>> class PersonClass4:
- ... def first_name(self):
- ... raise SilentAssertionError
- >>> p = PersonClass4()
- >>> t.render(Context({"person": p}))
- "My name is ."
2,方法调用仅仅在它没有参数时起作用,否则系统将继续查找下一个类型(列表索引查询)
3,显然一些方法有副作用,让系统访问它们是很愚蠢的,而且很可能会造成安全性问
题。
例如你有一个BankAccount对象,该对象有一个delete()方法,模板系统不应该允许做下面的事情
I will now delete this valuable data. {{ account.delete }}
为了防止这种状况,可以在方法里设置一个方法属性alters_data
如果设置了alters_data=True的话模板系统就不会执行这个方法:
- def delete(self):
- # Delete the account
- delete.alters_data = True
不合法的变量怎样处理
默认情况下如果变量不存在,模板系统会把它渲染成空string,例如:
- >>> 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 .'
系统会静悄悄地显示错误的页面,而不是产生一个异常,因为这种情况通常是人为的错误。
在现实情形下,一个web站点因为一个模板代码语法的错误而变得不可用是不可接受的。
我们可以通过设置Django配置更改Django的缺省行为,第10章扩展模板引擎会我们会讨论这个
玩玩Context对象
大多数情况下你初始化Context对象会传递一个字典给Context()
一旦你初始化了Context,你可以使用标准Python字典语法增减Context对象的items:
- >>> from django.template import Context
- >>> c = Context({"foo": "bar"})
- >>> c['foo']
- 'bar'
- >>> del c['foo']
- >>> c['foo']
- ''
- >>> c['newvariable'] = 'hello'
- >>> c['newvariable']
- 'hello'
Context对象是一个stack,你可以push()和pop()
如果你pop()的太多的话它将触发django.template.ContextPopException:
- >>> c = Context()
- >>> c['foo'] = 'first level'
- >>> c.push()
- >>> c['foo'] = 'second level'
- >>> c['foo']
- 'second level'
- >>> c.pop()
- >>> c['foo']
- 'first level'
- >>> c['foo'] = 'overwritten'
- >>> c['foo']
- 'overwritten'
- >>> c.pop()
- Traceback (most recent call last):
- ...
- django.template.ContextPopException
第10章你会看到使用Context作为stack自定义模板标签
模板标签和过滤器基础
我们已经提到模板系统使用内建的标签和过滤器
这里我们看看常见的,附录6包含了完整的内建标签和过滤器,你自己熟悉那个列表来了解可以做什么是个好主意
if/else
{% if %}标签计算一个变量值,如果是“true”,即它存在、不为空并且不是false的boolean值
系统则会显示{% if %}和{% endif %}间的所有内容:
- {% if today_is_weekend %}
- <p>Welcome to the weekend!</p>
- {% else %}
- <p>Get back to work.</p>
- {% endif %}
{% if %}标签接受and,or或者not来测试多个变量值或者否定一个给定的变量,例如:
- {% if athlete_list and coach_list %}
- Both athletes and coaches are available.
- {% endif %}
- {% if not athlete_list %}
- There are no athletes.
- {% endif %}
- {% if athlete_list or coach_list %}
- There are some athletes or some coaches.
- {% endif %}
- {% if not athlete_list or coach_list %}
- There are no athletes or there are some coaches.
- {% endif %}
- {% if athlete_list and not coach_list %}
- There are some athletes and absolutely no coaches.
- {% endif %}
{% if %}标签不允许同一标签里同时出现and和or,否则逻辑容易产生歧义,例如下面的标签是不合法的:
- {% if athlete_list and coach_list or cheerleader_list %}
如果你想结合and和or来做高级逻辑,只需使用嵌套的{% 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 %}
没有{% elif %}标签,使用嵌套的{% if %}标签可以做到同样的事情:
- {% if athlete_list %}
- <p>Here are the athletes: {{ athlete_list }}.</p>
- {% else %}
- <p>No athletes are available.</p>
- {% if coach_list %}
- <p>Here are the coaches: {{ coach_list }}.</p>
- {% endif %}
- {% endif %}
确认使用{% endif %}来关闭{% if %}标签,否则Django触发TemplateSyntaxError
for
{% for %}标签允许你按顺序遍历一个序列中的各个元素
Python的for语句语法为for X in Y,X是用来遍历Y的变量
每次循环模板系统都会渲染{% for %}和{% endfor %}之间的所有内容
例如,显示给定athlete_list变量来显示athlete列表:
- <ul>
- {% for athlete in athlete_list %}
- <li>{{ athlete.name }}</li>
- {% endfor %}
- </ul>
在标签里添加reversed来反序循环列表:
- {% for athlete in athlete_list reversed %}
- ...
- {% endfor %}
- {% for %}标签可以嵌套:
- {% for country in countries %}
- <h1>{{ country.name }}</h1>
- <ul>
- {% for city in country.city_list %}
- <li>{{ city }}</li>
- {% endfor %}
- </ul>
- {% endfor %}
系统不支持中断循环,如果你想这样,你可以改变你想遍历的变量来使得变量只包含你想遍历的值
类似的,系统也不支持continue语句,本章后面的“哲学和限制”会解释设计的原则
{% for %}标签内置了一个forloop模板变量,这个变量含有一些属性可以提供给你一些关于循环的信息
1,forloop.counter表示循环的次数,它从1开始计数,第一次循环设为1,例如:
- {% for item in todo_list %}
- <p>{{ forloop.counter }}: {{ item }}</p>
- {% endfor %}
2,forloop.counter0类似于forloop.counter,但它是从0开始计数,第一次循环设为0
3,forloop.revcounter表示循环中剩下的items数量,第一次循环时设为items总数,最后一次设为1
4,forloop.revcounter0类似于forloop.revcounter,但它是表示的数量少一个,即最后一次循环时设为0
5,forloop.first当第一次循环时值为True,在特别情况下很有用
安徽新华电脑学校专业职业规划师为你提供更多帮助【在线咨询】