在Python的Django框架中创建和使用模版

作者: 网络编程  发布:2019-09-03

在Python的Django框架中创建和使用模版,pythondjango

如何使用模板系统

让我们深入研究模板系统,你将会明白它是如何工作的。但我们暂不打算将它与先前创建的视图结合在一起,因为我们现在的目的是了解它是如何独立工作的。 。 (换言之, 通常你会将模板和视图一起使用,但是我们只是想突出模板系统是一个Python库,你可以在任何地方使用它,而不仅仅是在Django视图中。)

在Python代码中使用Django模板的最基本方式如下:

  •     可以用原始的模板代码字符串创建一个 Template 对象, Django同样支持用指定模板文件路径的方式来创建 Template 对象;
  •     调用模板对象的render方法,并且传入一套变量context。它将返回一个基于模板的展现字符串,模板中的变量和标签会被context值替换。

代码如下:

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

以下部分逐步的详细介绍
创建模板对象

创建一个 Template 对象最简单的方法就是直接实例化它。 Template 类就在 django.template 模块中,构造函数接受一个参数,原始模板代码。 让我们深入挖掘一下 Python的解释器看看它是怎么工作的。

转到project目录(在第二章由 django-admin.py startproject 命令创建), 输入命令 python manage.py shell 启动交互界面。

一个特殊的Python提示符

如果你曾经使用过Python,你一定好奇,为什么我们运行python manage.py shell而不是python。这两个命令都会启动交互解释器,但是manage.py shell命令有一个重要的不同: 在启动解释器之前,它告诉Django使用哪个设置文件。 Django框架的大部分子系统,包括模板系统,都依赖于配置文件;如果Django不知道使用哪个配置文件,这些系统将不能工作。

如果你想知道,这里将向你解释它背后是如何工作的。 Django搜索DJANGO_SETTINGS_MODULE环境变量,它被设置在settings.py中。例如,假设mysite在你的Python搜索路径中,那么DJANGO_SETTINGS_MODULE应该被设置为:'mysite.settings'。

当你运行命令:python manage.py shell,它将自动帮你处理DJANGO_SETTINGS_MODULE。 在当前的这些示例中,我们鼓励你使用`` python manage.py shell``这个方法,这样可以免去你大费周章地去配置那些你不熟悉的环境变量。

随着你越来越熟悉Django,你可能会偏向于废弃使用`` manage.py shell`` ,而是在你的配置文件.bash_profile中手动添加 DJANGO_SETTINGS_MODULE这个环境变量。

让我们来了解一些模板系统的基本知识:

>>> from django.template import Template
>>> t = Template('My name is {{ name }}.')
>>> print t

如果你跟我们一起做,你将会看到下面的内容:

<django.template.Template object at 0xb7d5f24c>

0xb7d5f24c 每次都会不一样,这没什么关系;这只是Python运行时 Template 对象的ID。

当你创建一个 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'

这里,块标签(block tag)指向的是`` {% notatag %}``,块标签与模板标签是同义的。

系统会在下面的情形抛出 TemplateSyntaxError 异常:

  •     无效的tags
  •     标签的参数无效
  •     无效的过滤器
  •     过滤器的参数无效
  •     无效的模板语法
  •     未封闭的块标签 (针对需要封闭的块标签)

如何使用模板系统 让我们深入研究模板系统,你将会明白它是如何工作的。但我们暂不打...

在前一章中,您可能已经注意到我们在示例视图中返回文本的一些特殊情况。 也就是说,HTML是直接在我们的Python代码中硬编码的,如下所示:

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

虽然这个技巧对于解释视图是如何工作的很方便,但是直接在你的视图中直接硬编码HTML并不是一个好主意。 原因如下:

  • 对页面设计的任何改变都需要修改Python代码。 网站的设计往往比底层的Python代码更加频繁,所以如果设计改变而不需要修改Python代码,那将是很方便的。

  • 这只是一个非常简单的例子。 一个普通的网页模板有数百行HTML和script脚本。 从这个混乱中解开和解决程序代码是一个噩梦。

  • 编写Python代码和设计HTML是两个不同的学科,大多数专业的Web开发环境将不同的人(甚至不同的部门)之间的责任分开。 设计人员和HTML / CSS编码人员不应该被要求编辑Python代码来完成他们的工作。

  • 如果程序员可以使用Python代码,设计人员可以同时使用模板,而不是等待另一个人完成编辑包含Python和HTML的单个文件,那么效率最高。

由于这些原因,将页面的设计与Python代码本身分开,使它更简洁,更易于维护。 我们可以用Django的模板系统来做到这一点,我们将在本章中讨论这个模板系统。

模板系统基础

Django模板是一个文本字符串,旨在将文档与其数据分开。 一个模板定义了占位符和各种基本逻辑(模板标签)的各个部分,用来管理文档应该如何显示。 通常,模板用于生成HTML,但Django模板同样能够生成任何基于文本的格式。

Django模板背后的哲学

如果您有编程背景,或者习惯了将编程代码直接混合到HTML中的语言,那么您应该记住,Django模板系统不仅仅是嵌入到HTML中的Python。 这种设计是:模板系统是为了表示图像,而不是程序逻辑。

我们从一个简单的示例模板开始。 这个Django模板描述了一个HTML页面,感谢一个人向公司下订单。 把它想成一个表格信:

<html>
<head>
    <title>Ordering notice</title>
</head>
<body>
    <h1>Ordering notice</h1>
    <p>Dear {{ person_name }},</p>
    <p>Thanks for placing an order from {{ company }}. It's scheduled to ship on {{ s
hip_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>

这个模板是基于HTML代码编写,其中包含了一些变量和模板标签。让我们一起来看看它:

  • 被一对大括号包围的任何文本(例如{{person_name}})是一个变量。这意味着“插入具有给定名称的变量的值”。我们如何指定变量的值?我们马上就会明白这一点。
  • 任何被花括号和百分号包围的文本(例如,{% if ordered_warranty%})是一个模板标签。标签的定义非常广泛:标签只是告诉模板系统去“做些什么”。
  • 这个示例模板包含一个for标签({% for item in item_list %})和一个if标签({%if ordered_warranty%})。 for标签的工作原理与Python中的for语句非常相似,可以循环使用序列中的每个项目。如你所期望的那样,一个if标签作为一个逻辑的“if”语句。在这种情况下,标签检查ordered_warranty变量的值是否为True。如果是,则模板系统将显示{%if ordered_warranty%}和{%else%}之间的所有内容。如果不是,模板系统将显示{%else%}和{%endif%}之间的所有内容。请注意,{%else%}是可选的。
  • 最后,这个模板的第二段包含一个过滤器的例子,这是改变变量格式的最方便的方法。在这个例子中,{{ ship_date|date:"F j, Y" }},我们将ship_date变量传递给日期过滤器,给日期过滤器赋予参数“F j,Y”。日期过滤器格式的日期格式是指定的格式。过滤器使用管道字符(|)作为对Unix管道的引用。

每个Django模板都可以访问几个内置的标签和过滤器,其中很多将在下面的章节中讨论。 附录E包含标签和过滤器的完整列表,最好熟悉这个列表,这样你就知道什么是可能的。 也可以创建自己的过滤器和标签; 我们将在第8章中介绍。

使用模板系统

Django项目可以配置一个或多个模板引擎(如果不使用模板,甚至可以设置为零)。 Django为其自己的模板系统(Django模板语言(DTL))提供了一个内置的后端。 Django 1.11还包括对流行的Jinja2的选择性支持。

如果您没有迫切的理由选择另一个后端,那么您应该使用DTL--尤其是在您正在编写可插入应用程序并且您打算分发模板时。 Django的contrib应用程序包含模板(如django.contrib.admin),使用DTL。 本章中的所有例子都将使用DTL。 有关更高级的模板主题(包括配置第三方模板引擎),请参阅第8章。

在我们开始在你的视图中实现Django模板之前,先让我们在DTL里面挖掘一下,看看它是如何工作的。 以下是您可以在Python代码中使用Django模板系统的最基本的方法:

  1. 通过将原始模板代码作为字符串提供来创建模板对象。
  2. 使用给定的一组变量(上下文)调用Template对象的render()方法。 这将以字符串形式返回完全呈现的模板,并根据上下文评估所有变量和模板标记。 使用Django自定义Python shell(python manage.py shell),看起来像这样:
>>> 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.

以下各节将更详细地介绍每个步骤。

一个特殊的Python提示符

如果你之前使用过Python,你可能会想知道为什么我们要运行python manage.py而不是python(或python3)。两个命令都会启动交互式解释器,但是manage.py shell命令有一个关键区别:在启动解释器之前,它会告诉Django要使用哪个设置文件。
Django的许多部分,包括模板系统,都依赖于你的设置,除非框架知道使用哪个设置,否则你将无法使用它们。如果你好奇的话,下面是它在后台的工作原理。 Django查找名为DJANGO_SETTINGS_MODULE的环境变量,应该将其设置为settings.py的导入路径。例如,假设mysite在Python路径上,DJANGO_SETTINGS_MODULE可能被设置为“mysite.settings”。
当你运行python manage.py shell时,该命令会为你设置DJANGO_SETTINGS_MODULE。在这些例子中你将需要使用python manage.py shell,否则Django会抛出异常。

创建模板对象

创建一个Template对象最简单的方法是直接实例化它。 Template类位于django.template模块中,构造函数接受一个参数,即原始模板代码。 让我们深入Python交互式解释器,看看它是如何在代码中工作的。 从第1章中创建的mysite项目目录中,输入python manage.py shell启动交互式解释器。

我们来看看一些模板系统的基础知识:

>>> from django.template import Template
>>> t = Template('My name is {{ name }}.')
>>> print (t)

如果你正在交互式命名窗口中,你会看到这样的东西:

<django.template.base.Template object at 0x030396B0>

0x030396B0每次都会有所不同,而且不相关; 这是一个Python的东西(如果你必须知道,这是Python为Template对象定义的一个“身份”,即内存地址)。

创建模板对象时,模板系统将原始模板代码编译为内部优化表单,以供呈现。 但是,如果您的模板代码包含任何语法错误,则对Template()的调用将导致TemplateSyntaxError异常:

>>> from django.template import Template
>>> t = Template('{% notatag %}')
Traceback (most recent call last):
File "", line 1, in ?
...
django.template.exceptions.TemplateSyntaxError: Invalid block tag on line 1: 'notatag'. Did you forget to register or load this tag?

这里的术语“块标记”是指{%notatag%}。 “块标签”和“模板标签”是同义词。 系统为以下任何情况引发TemplateSyntaxError异常:

  • Invalid tags
  • Invalid arguments to valid tags
  • Invalid filters
  • Invalid arguments to valid filters
  • Invalid template syntax
  • Unclosed tags (for tags that require closing tags)
呈现模板

一旦你有了一个Template对象,你可以通过给它一个上下文来传递它的数据。 上下文只是一组模板变量名称及其相关的值。 一个模板使用它来填充它的变量并评估它的标签。 上下文在Django中由Context类生成,它位于django.template模块中。 它的构造函数有一个可选的参数:将变量名映射到变量值的字典。 使用上下文调用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.'
字典和上下文

Python字典是已知键和变量值之间的映射。 上下文类似于字典,但是上下文提供了额外的功能,如第8章所述。

变量名称必须以字母(A-Z或a-z)开头,可能包含更多字母,数字,下划线和点。 (点是我们稍后会遇到的特例。)变量名称区分大小写。 下面是一个模板编译和渲染的例子,使用与本章开头部分相似的模板:

>>> from django.template import Template, Context
>>> raw_template = """<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>
...
... {% 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>"""
>>> t = Template(raw_template)
>>> import datetime
>>> c = Context({'person_name': 'John Smith',
...     'company': 'Outdoor Equipment',
...     'ship_date': datetime.date(2017, 7, 2),
...     'ordered_warranty': False})
>>> t.render(c)
"<p>Dear John Smith,</p>nn<p>Thanks for placing an order from Outdoor Equipment. It
's scheduled tonship on July 2,2017.</p>nnn<p>You didn't order a warranty, so you
're on your own whennthe products inevitably stop working.</p>nnn<p>Sincerely,<br
 />Outdoor Equipment</p>"
  • 首先,我们导入模板和上下文,它们都位于模块django.template中。
  • 我们将模板的原始文本保存到变量raw_template中。请注意,我们使用三重引号来指定字符串,因为它包装了多行;相反,单引号内的字符串不能包含多行。
  • 接下来,我们通过将raw_template传递给Template类的构造函数来创建一个模板对象t。
  • 我们从Python的标准库中导入日期时间模块,因为我们将在下面的语句中使用它。
  • 然后,我们创建一个Context对象,c。 Context构造函数需要一个Python字典,它将变量名称映射到值。在这里,例如,我们指定person_name是“John Smith”,公司是“Outdoor Equipment”,等等。
  • 最后,我们在模板对象上调用render()方法,将其传递给上下文。这将返回呈现的模板,即它将模板变量替换为变量的实际值,并执行任何模板标记。请注意,由于ordered_warranty变量评估为False,因此显示“您没有订购保修”段落。另请注意,日期为2017年7月2日,根据格式字符串“F j,Y”显示。 (稍后我会解释日期过滤器的格式字符串。)

如果您是Python新手,您可能会想知道为什么此输出包含换行符(“ n”)而不是显示换行符。 这是因为Python交互式解释器中的一个微妙之处:对t.render(c)的调用返回一个字符串,默认情况下交互式解释器显示字符串的表示形式,而不是字符串的打印值。 如果要查看带换行符的字符串而不是“ n”字符,请使用print函数:print(t.render(c))。

这些是使用Django模板系统的基础:只需编写一个模板字符串,创建一个Template对象,创建一个Context,然后调用render()方法。

多个上下文,相同的模板

一旦你有一个模板对象,你可以通过它来渲染多个上下文。 例如:

>>> 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()就可以了:

# 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的模板渲染引擎要慢几个数量级。

上下文变量查找

在目前为止的例子中,我们已经在上下文中传递了简单的值 - 主要是字符串,还有一个datetime.date的例子。 但是,模板系统优雅地处理更复杂的数据结构,如列表,词典和自定义对象。 遍历Django模板中复杂数据结构的关键是点字符(“.”)。

使用点来访问对象的字典键,属性,方法或索引。 举几个例子来说明这一点。 例如,假设你将一个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(2017, 5, 2)
>>> d.year
2017
>>> 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 2017.'

这个例子使用了一个自定义类,演示了变量点也允许在任意对象上访问属性:

>>> 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。

Python列表索引

提醒:Python列表有从0开始的索引。 第一项是索引0,第二项是索引1,依此类推。

点查找可以这样概括:当模板系统在变量名中遇到一个点时,它会按以下顺序尝试以下查找:

  • 字典查找(例如,foo [“bar”])
  • 属性查找(例如,foo.bar)
  • 方法调用(例如,foo.bar())
  • 列表索引查找(例如,foo [2])

系统使用可用的第一个查找类型。 这是短路逻辑。 点查找可以嵌套多层。 例如,下面的例子使用{{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.'
方法调用行为

方法调用比其他查找类型稍微复杂一些。 这里有一些事情要记住:

  • 如果在方法查找期间,方法引发异常,则将传播异常,除非异常具有值为True的属性silent_variable_failure。 如果异常具有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有意限制了模板中可用的逻辑处理的数量,所以无法将参数传递给从模板中访问的方法调用。 数据应该在视图中计算,然后传递给模板进行显示。
  • 显然,有些方法有副作用,最多也是愚蠢的,甚至可能是安全漏洞,允许模板系统访问它们。
  • 比方说,你有一个拥有delete()方法的BankAccount对象。 如果模板包含{{account.delete}}之类的内容,其中account是BankAccount对象,则在模板呈现时,对象将被删除! 为了防止这种情况,在方法上设置函数属性alters_data:
 def delete(self):
     # Delete the account
     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 .'

这种行为比引发异常更好,因为它的目的是对人为错误有弹性。 在这种情况下,所有的查找失败,因为变量名称有错误的大小写或名称。 在现实世界中,由于小的模板语法错误,网站变得不可访问是不可接受的。

本文由王中王开奖结果发布于网络编程,转载请注明出处:在Python的Django框架中创建和使用模版

关键词: