在前一章中,你可能已经注意到我们在例子视图中返回文本的方式有点特别。 也就是说,HTML被直接硬编码在 Python 代码之中。
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
排版和字体很有问题,看得很累,尤其是代码区,没有英文原版的好 另外可以参考一下csdn的,看得舒服一些 http://blog.csdn.net/cpk154505/article/details/8825277
尽管这种技术便于解释视图是如何工作的,但直接将HTML硬编码到你的视图里却并不是一个好主意。 让我们来看一下为什么:
- 对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多。
- Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。 设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作。
- 程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。
基于这些原因,将页面的设计和Python的代码分离开会更干净简洁更容易维护。 我们可以使用 Django的 模板系统 (Template System)来实现这种模式,这就是本章要具体讨论的问题。
模板是一个文本,用于分离文档的表现形式和内容。 模板定义了占位符以及各种用于规范文档该如何显示的各部分基本逻辑(模板标签)。 模板通常用于产生HTML,但是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 {{ 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>
1.请问 {% for item in item_list %} 与 {{person_name}} 有什么区别? 2.{% ... %}是什么意思? 3.{{ person_name }} 指的是变量么,为什么要用两个 {{}} 呢?
该模板是一段添加了些许变量和模板标签的基础 HTML 。 让我们逐步分析一下:
用两个大括号括起来的文字(例如 {{ person_name }} )称为 变量(variable) 。这意味着在此处插入指定变量的值。 如何指定变量的值呢? 稍后就会说明。
被大括号和百分号包围的文本(例如 {% if ordered_warranty %} )是 模板标签(template tag) 。标签(tag)定义比较明确,即: 仅通知模板系统完成某些工作的标签。
这个例子中的模板包含一个for标签( {% for item in item_list %} )和一个if 标签({% if ordered_warranty %} )
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过滤器,同时指定参数”F j,Y”。date过滤器根据参数进行格式输出。 过滤器是用管道符(|)来调用的,具体可以参见Unix管道符。
{{ship_date|date:”F j, Y” }}……指定参数”F j,Y” 前双引号用反了。建议改为: {{ship_date|date:“F j, Y” }}……指定参数“F j,Y”
tags和filters看官方文档就好:https://docs.djangoproject.com/en/3.2/ref/templates/builtins/,要适应查看官方文档
Django 模板含有很多内置的tags和filters,我们将陆续进行学习. 附录F列出了很多的tags和filters的列表,熟悉这些列表对你来说是个好建议. 你依然可以利用它创建自己的tag和filters。这些我们在第9章会讲到。
看http://www.djangobook.com/en/2.0/index.html,Appendix E: Built-in Template Tags and Filters,应为附录E
如何使用模板系统
让我们深入研究模板系统,你将会明白它是如何工作的。但我们暂不打算将它与先前创建的视图结合在一起,因为我们现在的目的是了解它是如何独立工作的。 。 (换言之, 通常你会将模板和视图一起使用,但是我们只是想突出模板系统是一个Python库,你可以在任何地方使用它,而不仅仅是在Django视图中。)
在Python代码中使用Django模板的最基本方式如下:
翻译不太好, 1. Create a Template object by providing the raw template code as a string. 1. 创建一个 Template 对象,将模板代码放入一个 string 中作为参数。
2. Call the render() method of the Template object with a given set of variables (the context). This returns a fully rendered template as a string, with all of the variables and template tags evaluated according to the context. 2. 调用模板(template)对象的 render() 方法,把一组变量作为上下文(context)传入。这么做将会把模板(template)展开,将所有变量(variables)和标签(tags)都赋予相应的值,并作为 string 返回,
- 可以用原始的模板代码字符串创建一个
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.
ImportError: Settings cannot be imported, because environment variable DJANGO_SETTINGS_MODULE is undefined. 现在按这篇文章的方法来做 方法一: >>> from django.template import Template, Context >>> from django.conf import settings >>> settings.configure() >>> t = Template('My name is {{ my_name }}.') >>> c = Context({'my_name': 'Daryl Spitzer'}) >>> t.render(c) ok通过 方法二: 先确定python的路径 $ which python2.5 /usr/bin/python2.5 $ export PYTHONPATH=/usr/bin/python2.5:~/share $ export DJANGO_SETTINGS_MODULE=test.settings 其中:~/share是django工程test所在的路径 测试一下 >>> from django.template import Template, Context >>> from django.conf import settings >>> t = Template('My name is {{ my_name }}.') >>> c = Context({'my_name': 'Daryl Spitzer'}) >>> t.render(c) ok通过 在Ubuntu中将 export PYTHONPATH=/usr/bin/python2.5:~/share export DJANGO_SETTINGS_MODULE=test.settings 加到~/.profile文件中,每次登录就自动设置了
楼上,ipython启动不能正常使用呀。我试过了,照样报错的。还是得用./manage.py shell启动才能正常使用。不过也奇怪,启动后,显示的是ipython的信息。
hohooo的方法是对的:增加下面几行。 >>> from django.conf import settings >>> settings.configure()
经测试,在代码头部加上 >>> from django.conf import settings >>> settings.configure() 为可行办法之一(python 2.7;django 1.5)
除了python manage.py shell这种方式外还可以使用下面的方法: >>> from django.conf import settings >>> settings.configure() >>> from django import template >>> t = template.Template('My name is {{ name }}.') >>> c = template.Context({'name': 'yixiaohan'}) >>> print t.render(c) My name is yixiaohan. >>> c = template.Context({'name': 'xiaowangge'}) >>> print t.render(c) My name is xiaowangge.
除了python manage.py shell这种方式外还可以使用下面的方法: (来自http://blog.csdn.net/xiaowanggedege/article/details/8651236) >>> from django.conf import settings >>> settings.configure() >>> from django import template >>> t = template.Template('My name is {{ name }}.') >>> c = template.Context({'name': 'yixiaohan'}) >>> print t.render(c) My name is yixiaohan. >>> c = template.Context({'name': 'xiaowangge'}) >>> print t.render(c) My name is xiaowangge.
>>> from django.conf import settings >>> settings.configure() >>> t = template.Template('my name is {{ name }}.') Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'template' is not defined >>> from django.template import Template, Context >>> t = template.Template('my name is {{ name }}.') Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'template' is not defined >>> t = Template('my name is {{ name }}.') >>> c = Context({'name': 'Fred'}) >>> print t.render(c) my name is Fred.
在代码头部加上 >>> from django.conf import settings >>>settings.configure() 为可行办法(python 3.3;django 1.5),新版本的python还有很多细节变化,print要加上()才能正常使用。 另外一种方法是通过cmd或者shell在manage.py目录下输入命令python manage.py shell来启动shell,蛋疼的是windows下python的shell窗口时在cmd中的……
>>> from django.conf import settings >>> settings.configure() >>> t = template.Template('my name is {{ name }}.') >>> c = template.Context({'name': 'vincent'}) >>> print(t.render(c)) my name is vincent.
>>> from django.conf import settings<br> >>> settings.configure()<br> >>> t = template.Template('my name is {{ name }}.')<br> >>> c = template.Context({'name': 'vincent'})<br> >>> print(t.render(c))<br> my name is vincent.
用 python manage.py shell 可以 另外~~ 加這兩行 from django.conf import settings settings.configure() 也可以。
>>> from django.conf import settings >>> settings.configure() 亲测Django 1.8 可用。 多谢楼上各位!
为什么我加入了 from django.conf import settings settings.configure() 还是不行?会报错django.core.exceptions.ImproperlyConfigured: No DjangoTemplates backend is configured.
以下部分逐步的详细介绍
创建模板对象
创建一个 Template 对象最简单的方法就是直接实例化它。 Template 类就在 django.template 模块中,构造函数接受一个参数,原始模板代码。 让我们深入挖掘一下 Python的解释器看看它是怎么工作的。
转到project目录(在第二章由 django-admin.py startproject 命令创建), 输入命令 python manage.py shell 启动交互界面。
应该是“转到mysite项目目录”而不是“转到project目录”。 见原文: From the mysite project directory created by django-admin.py startproject (as covered in Chapter 2), type python manage.py shell to start the interactive interpreter.
一个特殊的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’。
新版本的Django里,DJANGO_SETTINGS_MODULE貌似在manage.py文件中设置os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite_evo.settings")
DJANGO_SETTINGS_MODULE是在manage.py里面设置的,本质是一个环境变量,它默认指向应用下的settings.py,也就是mysite.settings,这里翻译有误。
这是manage.py里指定的一个对应,manage.py里的代码是os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
楼上老虎正解,在django 1.5中,DJANGO_SETTINGS_MODULE是在manage.py里面设置的,本质是一个环境变量,它默认指向应用下的settings.py,也就是mysite.settings.(本书所用django版本较老)
当你运行命令:python manage.py shell,它将自动帮你处理DJANGO_SETTINGS_MODULE。 在当前的这些示例中,我们鼓励你使用python manage.py shell这个方法,这样可以免去你大费周章地去配置那些你不熟悉的环境变量。
随着你越来越熟悉Django,你可能会偏向于废弃使用manage.py shell ,而是在你的配置文件.bash_profile中手动添加 DJANGO_SETTINGS_MODULE这个环境变量。
假设你的路径是这样的: /home/athos/django/mysite/manage.py 则在/home/athos/.bash_profile 增加两行: export PYTHONPATH=$PYTHONPATH:/home/athos/django/mysite/ export DJANGO_SETTINGS_MODULE=mysite.settings 重新登录即生效
让我们来了解一些模板系统的基本知识:
>>> from django.template import Template
>>> t = Template('My name is {{ name }}.')
>>> print t
我想在ipython下测试结果发现提示那个setting模块变量未设置。因为我的ipython不是安装的而是直接运行,如何解决啊?如果setup ipython就没事了,我不想将ipython安装进python的package里
如果你跟我们一起做,你将会看到下面的内容:
<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'
都这么憨啊,上面说了模板标签是这样的{% if xxx%} {% else xxx%} 这里的{% notatag %}不是django里定义的模板标签类型,所以报错了,就 跟关键字一样么,没有定义这个关键字。
这里,块标签(block tag)指向的是{% notatag %},块标签与模板标签是同义的。
系统会在下面的情形抛出 TemplateSyntaxError 异常:
- 无效的tags
- 标签的参数无效
- 无效的过滤器
- 过滤器的参数无效
- 无效的模板语法
- 未封闭的块标签 (针对需要封闭的块标签)
一旦你创建一个 Template 对象,你可以用 context 来传递数据给它。 一个context是一系列变量和它们值的集合。
context在Django里表现为 Context 类,在 django.template 模块里。 她的构造函数带有一个可选的参数: 一个字典映射变量和它们的值。 调用 Template 对象 的 render() 方法并传递context来填充模板:
>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'Stephane'})
>>> t.render(c)
u'My name is Stephane.'
我们必须指出的一点是,t.render(c)返回的值是一个Unicode对象,不是普通的Python字符串。 你可以通过字符串前的u来区分。 在框架中,Django会一直使用Unicode对象而不是普通的字符串。 如果你明白这样做给你带来了多大便利的话,尽可能地感激Django在幕后有条不紊地为你所做这这么多工作吧。 如果不明白你从中获益了什么,别担心。你只需要知道Django对Unicode的支持,将让你的应用程序轻松地处理各式各样的字符集,而不仅仅是基本的A-Z英文字符。
python3 中的所有字符串都是Unicode编码的字符序列, 与django的模板中的字符串类型一致. 这里作者指的是与 以UTF-8或者CP-1252编码的普通Python2字符串 不同
字典和Contexts
Python的字典数据类型就是关键字和它们值的一个映射。 Context 和字典很类似, Context 还提供更多的功能,请看第九章。
变量名必须由英文字符开始 (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(2009, 4, 2),
... 'ordered_warranty': False})
>>> t.render(c)
u"<p>Dear John Smith,</p>\n\n<p>Thanks for placing an order from Outdoor
Equipment. It's scheduled to\nship on April 2, 2009.</p>\n\n\n<p>You
didn't order a warranty, so you're on your own when\nthe products
inevitably stop working.</p>\n\n\n<p>Sincerely,<br />Outdoor Equipment
</p>"
为什么返回的结果是 You didn't order a warranty, so you're on your own when\nthe products inevitably stop workin,而不是Your warranty information will be included in the packaging.我不太明白 {% if ordered_warranty %}的意思.
让我们逐步来分析下这段代码:
首先我们导入 (import)类 Template 和 Context ,它们都在模块 django.template 里。
我们把模板原始文本保存到变量 raw_template 。注意到我们使用了三个引号来 标识这些文本,因为这样可以包含多行。
我们从Python的标准库导入 datetime 模块,以后我们将会使用它。
然后,我们创建一个 Context 对象, c 。 Context 构造的参数是Python 字典数据类型。 在这里,我们指定参数 person_name 的值是 'John Smith' , 参数company 的值为 ‘Outdoor Equipment’ ,等等。
最后,我们在模板对象上调用 render() 方法,传递 context参数给它。 这是返回渲染后的模板的方法,它会替换模板变量为真实的值和执行块标签。
注意,warranty paragraph显示是因为 ordered_warranty 的值为 True . 注意时间的显示, April 2, 2009 , 它是按 'F j, Y' 格式显示的。
如果你是Python初学者,你可能在想为什么输出里有回车换行的字符('\n' )而不是 显示回车换行? 因为这是Python交互解释器的缘故: 调用 t.render(c) 返回字符串, 解释器缺省显示这些字符串的 真实内容呈现 ,而不是打印这个变量的值。 要显示换行而不是 '\n' ,使用 print 语句: print t.render(c) 。
这就是使用Django模板系统的基本规则: 写模板,创建 Template 对象,创建 Context , 调用 render() 方法。
同一模板,多个上下文
一旦有了 模板 对象,你就可以通过它渲染多个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,只进行 一次模板创建然后多次调用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 传递的简单参数值主要是字符串,还有一个 datetime.date 范例。 然而,模板系统能够非常简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。
最好是用几个例子来说明一下。 比如,假设你要向模板传递一个 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)
u'Sally is 43 years old.'
t = Template('{{ person.name }} is {{ person.age }} years old.') 这里的person 要和下面的c = Context({'person': person})对应,如上面改成了persons123.name,则下面context的时候也要写成{'persons123':person}
楼顶 你看看前面的设置就知道在哪里设置Context了 >>> from django.template import Template, Context >>> from django.conf import settings >>> settings.configure() >>> t = Template('My name is {{ my_name }}.') >>> c = Context({'my_name': 'Daryl Spitzer'}) >>> t.render(c) ok通过
person = {'name':'Jesse', 'age':'30'} t = Template('{{ Person.name}} is {{person.age}} years old.') c = Context({'Person': person}) print t.render(c) 打印出来是Jesse is years old.。少了年龄
楼上,你打印出来少了年龄,是因为你在初始化Template时年龄用的是‘person.age’,正确的应该是‘Person.age’。从Context传入的变量名为‘Person’,Django大小写是敏感的。(主要是这个栗子容易造成误解)
同样,也可以通过句点来访问对象的属性。 比方说, Python 的 datetime.date 对象有 year 、 month 和 day 几个属性,你同样可以在模板中使用句点来访问这些属性:
>>> 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)
u'The month is 5 and the year is 1993.'
这个例子使用了一个自定义的类,演示了通过实例变量加一点(dots)来访问它的属性,这个方法适用于任意的对象。
self 是不是类似形参的形参?传递值(的算法)? 下面这行的代码是为了指出后面p.first_name 和p.last_name的计算方法?即分别对应Person('Jhon',Smith')的参数值? ------self.first_name, self.last_name = first_name, last_name
@深圳远程教育 实例化Python类的方法不就是调用这个类的方法吗? @浮云比翼 vs @Scar_l Template的person对应Context中的键,而Context中的person仅仅是个键,换成其它任意一个变量参数都可以,Context值中的Person则是实例化自定义的Person这个类
{‘person’:Person(‘John’,‘Smith’)}这是传给Context一个字典,第一个person是键,后面是一个类型为类的值,这个类是 Person,这样理解应该没错吧
我在t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.') 这行提示我t 是 invalid syntax 有人知道怎么办吗
self.first_name, self.last_name = first_name, last_name 这一行后面需要再敲回车,记住一定要有,我的是python2.6.6
这里的person只是字典里的键,和类没关系,最后渲染的时候调用Person类,因为self的原因所以直接调用了,大家可以试试把person改成其他的名字,都是可以的,但是要上下呼应
把在三条改一下,应该能懂了: >>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.') >>> c = Context({'person': Person('John', 'Smith')}) >>> t.render(c) 更改如下: >>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.') >>> tempc = Person('John', 'Smith') >>> c = Context({'person': tempc}) >>> t.render(c)
可以将Context({'person':Person('John','Smith')})理解为: person=Person('John','Smith'),这样person就是Person类的一个实例,然后通过Template函数调用person.first_name,last_name,其实Context里的参数‘person’键对应的值就是一个类,只不过我们原先看到的是字典或者字符窜比较容易理解,但是字典和字符窜也是类,只不过可以直接赋值,在python中,是可以继承dict,list,tuple的,因为这些都是类
>>> class Person(): ... def __init__(self,name,age): ... self.name = name ... self.age = age ... >>> p = Person("gang",17) >>> p <Person object at 0x0000000004C7E9B0> >>> p.name 'gang' >>> p.age 17 >>> t = Template("i am {{p.name}}, age is {{p.age}}") >>> c = Context({"p":p}) >>> t.render(c) 'i am gang, age is 17'
为什么定义的函数必须是__init__?不是说函数名不影响作用的吗?返回的错误是 File "<console>", line 1,in <module> /n TypeError: object() takes no parameters
python类方法必须增加一个额外的形参,而且它 必须处于第一个形参的位置, 但是在调用类方法时不要也不必为这个额外的形参传值, python 会自动代劳。这个特别的变量引用对象本身,按照惯例它被命名为 self。 也就是C++中的this指针, 指向调用该类方法的对象
>>> 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.'
python3 默认就是新式类了,python要用新式类就得带上object这个参数。 新式类 在多重继承的时候是广度优先遍历,老式类是深度优先,其他请自行百度。。。
python3 默认就是新式类了,python2要用新式类就得带上object这个参数。 新式类 在多重继承的时候是广度优先遍历,老式类是深度优先,其他请自行百度。。。
在pycharm4.5.2的python console中输入这段代码,到t = Template('Hello, {{person.first_name}} {{person.last_name}}.')这句后弹出‘ImproperlyConfigured: Requested setting TEMPLATES, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings. ’不解
点语法也可以用来引用对象的 方法。 例如,每个 Python 字符串都有 upper() 和 isdigit() 方法,你在模板中可以使用同样的句点语法来调用它们:
>>> from django.template import Template, Context
>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
>>> t.render(Context({'var': 'hello'}))
u'hello -- HELLO -- False'
>>> t.render(Context({'var': '123'}))
u'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)
u'Item 2 is carrots.'
不允许使用负数列表索引。 像 {{ items.-1 }} 这样的模板变量将会引发TemplateSyntaxError
关于 {{ person.name.upper }}是否调用upper 方法还要看person 字典本身的内容, 比如person如下: person = {'name': {'upper': 'haha'}, 'age': '43'} 那么就不是调用upper方法 而是 person['name']['upper']
什么正解啊,这根本就不是一回事。建议看看下面话,“当模板系统在变量名中遇到点时,按照以下顺序尝试进行查找:”还有这句“句点查找可以多级深度嵌套。”这里之所以不是当作upper函数是因为“句点查找可以多级深度嵌套。”这句话,因为.查找是有顺序的,先找字典类型再查找方法,你这里仅仅是因为恰巧用了一个跟upper函数同名的字典属性而已,当然得按照顺序把字典属性扒完了再继续看方法了,LS两位这种取巧的自以为是谁都能看出来,别再误解后人好不好啊?
Python 列表类型
一点提示: Python的列表是从0开始索引。 第一项的索引是0,第二项的是1,依此类推。
句点查找规则可概括为: 当模板系统在变量名中遇到点时,按照以下顺序尝试进行查找:
- 字典类型查找 (比如
foo["bar"]) - 属性查找 (比如
foo.bar) - 方法调用 (比如
foo.bar()) - 列表类型索引查找 (比如
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)
u'SALLY is 43 years old.'
context({person:person}),前一个变量换成其它名称更利于理解,例如temp,同样上面person.name.upper等的person也要换了
>>> persons = {'name':'Tom','age':28} >>> t = Template('{{person.name.upper}} is {{person.age}} years old.') >>> t.render(Context({'person':persons})) u'TOM is 28 years old.' >>> person多个s就更清楚了。
方法调用比其他类型的查找略为复杂一点。 以下是一些注意事项:
在方法查找过程中,如果某方法抛出一个异常,除非该异常有一个 silent_variable_failure 属性并且值为 True ,否则的话它将被传播。如果异常被传播,模板里的指定变量会被置为空字符串,比如:
传播可译为引发或者抛出 如果异常被传播应改为如果异常被处理,原文If the exception does have a silent_variable_failure attribute, the variable will render as an empty string
我敲第三行:def first_name(self): 然后回车,提示出错,指向def中的f,提示IndentationError:expected an indented block 什么情况?我也木有学过python.......
作者没写错,也没写反。属性不为真的话,异常传播(使用异常值往下处理),而这个异常会使得指定变量变为空字符串。属性为真,则异常不传播,表现为方法没有被调用,方法不会对变量产生影响。下面的例子就表现在不会对account进行delete方法。大家仔细读一读,感受一下“除非”“否则”这两个词。django模板的方法是不允许调用变量的。
PersonClass4这个类里的方法抛出了silent_variable_failure = True,最后变量被置空了,说明异常是被传播了还是没被传播?
在方法查找过程中,如果某方法抛出一个异常,除非该异常有一个 silent_variable_failure 属性并且值为 True ,否则的话它将被传播。如果该异常 确有 属性 silent_variable_failure ,那么(所查找)变量将被渲染为空字符串, 这个是1.0的注释,可以对应着看看。
这里应该是“如果异常不被传播,程序执行不报错中断,模板里指定变量的部分被置为空字符串”。silent_variable_failure=True 可以理解为对异常保持“沉默”,即不上传异常。
>>> 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(AssertionError):
... silent_variable_failure = True
>>> class PersonClass4:
... def first_name(self):
... raise SilentAssertionError
>>> p = PersonClass4()
>>> t.render(Context({"person": p}))
u'My name is .'
在 p = PersonClass4() t.render(Context({"person": p})) 运行时得不到示例结果,而是抛出异常: global name 'SilentAssertionError' is not defined 有人知道为什么吗?
在 p = PersonClass4() t.render(Context({"person": p})) 运行时得不到示例结果,而是抛出异常: global name 'SilentAssertionError' is not defined 有人知道为什么吗? 原来进入IPython的方式是“python manage.py shell” 会出现上面的问题,现在改成“ipython” “from import django.conf import settings” “settings.configure()” 就没上面的问题了。应该是路径配置上出了问题。
加入这两句也可以 import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") 该句代码是从manage.py那里拷贝过来的
t.render(Context({"person": p})) 这句就报错, File "<console>", line 3, in first_name AssertionError: foo
raise AssertionError,"foo" 输完一按回车就出错 显示 在逗号那有个 语法错误 line3 SyntaxError :invalid syntax 这是为啥
仅在方法无需传入参数时,其调用才有效。 否则,系统将会转移到下一个查找类型(列表索引查找)。
显然,有些方法是有副作用的,好的情况下允许模板系统访问它们可能只是干件蠢事,坏的情况下甚至会引发安全漏洞。
例如,你的一个 BankAccount 对象有一个 delete() 方法。 如果某个模板中包含了像 {{ account.delete }}这样的标签,其中account 又是BankAccount 的一个实例,请注意在这个模板载入时,account对象将被删除。
要防止这样的事情发生,必须设置该方法的 alters_data 函数属性:
def delete(self):
# Delete the account
delete.alters_data = True
def delete(self): # Delete the account alters_data = True 或者 def delete(self): # Delete the account delete.alters_data = True 应该都是一样的吧
不能缩进 class TestCls(object): value = '123' def do() self.__class__.value = '456' do.alters_data = True
不用缩进。这是在给delete 这个方法设置一个属性。设置属性当然要在这个方法外面设置。如果缩进的话,就代表在delete方法里使用delete,这又不是递归,会报错的吧。
模板系统不会执行任何以该方式进行标记的方法。 接上面的例子,如果模板文件里包含了 {{ account.delete }} ,对象又具有 delete()方法,而且delete() 有alters_data=True这个属性,那么在模板载入时, delete()方法将不会被执行。 它将静静地错误退出。
默认情况下,如果一个变量不存在,模板系统会把它展示为空字符串,不做任何事情来表示失败。 例如:
>>> from django.template import Template, Context
>>> t = Template('Your name is {{ name }}.')
>>> t.render(Context())
u'Your name is .'
>>> t.render(Context({'var': 'hello'}))
u'Your name is .'
>>> t.render(Context({'NAME': 'hello'}))
u'Your name is .'
>>> t.render(Context({'Name': 'hello'}))
u'Your name is .'
系统静悄悄地表示失败,而不是引发一个异常,因为这通常是人为错误造成的。 这种情况下,因为变量名有错误的状况或名称, 所有的查询都会失败。 现实世界中,对于一个web站点来说,如果仅仅因为一个小的模板语法错误而造成无法访问,这是不可接受的。
在英文版页面找到以下评注,希望对大家有用: Daphna May 15th, 2009 3:43 a.m. "By default" implies that there's a way to change this behavior (and indeed it would be interesting to know how you can track these failures). You could say something about how to change it to refer readers to information about it. MJ Dec 7th, 2010 10:50 a.m. The below code will raise an exception for invalid Template variable. settings.py class InvalidVarException(object): def mod(self, missing): try: missing_str=unicode(missing) except: missing_str='Failed to create string representation' raise Exception('Unknown template variable %r %s' % (missing, missing_str)) def contains(self, search): if search=='%s': return True return False TEMPLATE_DEBUG=True TEMPLATE_STRING_IF_INVALID = InvalidVarException()
玩一玩上下文(context)对象
多数时间,你可以通过传递一个完全填充(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'
基本的模板标签和过滤器
像我们以前提到过的,模板系统带有内置的标签和过滤器。 下面的章节提供了一个多数通用标签和过滤器的简要说明。
标签
{% if %} 标签检查(evaluate)一个变量,如果这个变量为真(即,变量存在,非空,不是布尔值假),系统会显示在 {% if %} 和 {% endif %} 之间的任何内容,例如:
{% 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 %}
Python 的“真值”
在Python和Django模板系统中,以下这些对象相当于布尔值的False
- 空列表(
[]) - 空元组(
()) - 空字典(
{}) - 空字符串(
'') - 零值(
0) - 特殊对象
None - 对象
False(很明显) - 提示:你也可以在自定义的对象里定义他们的布尔值属性(这个是python的高级用法)。
为何我if的值是0还是还是执行了呢,帮我看看下面代码 def table(request): list_a = {'condition1':'0','condition2':'1','name1':'Tom','name2':'Jone'} list_b = {'Zhang':'33','Wang':'31','Li':'32','Zhao':'33'} dic = {'list1':list_a,'list2':list_b} return render_to_response('table.html',dic) 模版文件如下: {% if list1.condition1 %} <li> {{ list1.condition1 }} <li> Your Name is: {{ list1.name1 }} </li> {% elif list1.condition2 %} <li> {{ list1.condition2 }} <li> Your Name is: {{ list1.name2 }} </li> {% else %} <li> Your Name is: Nothing </li> {% endif %} 显示如下: 测试 if 语句: 0 Your Name is: Tom 奇怪了,判断值为0,居然还可以执行呢?
除以上几点以外的所有东西都视为True
{% if %} 标签接受 and , or 或者 not 关键字来对多个变量做判断 ,或者对变量取反( not ),例如: 例如:
标签pyhon manage.py shell里不能运行呢? 然后view.py里试,会直接打印出代码: def lable_test(request): html = """{% if today_is_weekend %} <p>Welcome to the weekend!</p> {% endif %}""" return HttpResponse(html)
{% 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 %}
系统不支持用圆括号来组合比较操作。 如果你确实需要用到圆括号来组合表达你的逻辑式,考虑将它移到模板之外处理,然后以模板变量的形式传入结果吧。 或者,仅仅用嵌套的{% if %}标签替换吧,就像这样:
{% if athlete_list %}
{% if coach_list or cheerleader_list %}
We have athletes, and either coaches or cheerleaders!
{% endif %}
{% endif %}
多次使用同一个逻辑操作符是没有问题的,但是我们不能把不同的操作符组合起来。 例如,这是合法的:
<%if A and/or B%> <%if A or not B%> <%if not A and B%> <%if A and B and C …… %> <%if A or B or C …… %> 类似以上都是合法的,但下面的是非法的: <%if A and b or c%>
{% if athlete_list or coach_list or parent_list or teacher_list %}
并没有 {% elif %} 标签, 请使用嵌套的{% if %} 标签来达成同样的效果:
现在已经有了{% elif %} 标签: https://docs.djangoproject.com/en/dev/ref/templates/builtins/ ------- 以后的链接,可能失效,官网一定会更新,大家注意评论时间戳。
{% 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 %}
{% 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>
class person: def __init__(self,name): self.name = name athlete_list=[person('John'),person('Joe')] c = Context({'athlete_list':athlete_list}) t.render(c)
给标签增加一个 reversed 使得该列表被反向迭代:
{% for athlete in athlete_list reversed %}
...
{% endfor %}
可以嵌套使用 {% for %} 标签:
{% for athlete in athlete_list %}
<h1>{{ athlete.name }}</h1>
<ul>
{% for sport in athlete.sports_played %}
<li>{{ sport }}</li>
{% endfor %}
</ul>
{% endfor %}
在执行循环之前先检测列表的大小是一个通常的做法,当列表为空时输出一些特别的提示。
{% if athlete_list %}
{% for athlete in athlete_list %}
<p>{{ athlete.name }}</p>
{% endfor %}
{% else %}
<p>There are no athletes. Only computer programmers.</p>
{% endif %}
因为这种做法十分常见,所以for 标签支持一个可选的{% empty %} 分句,通过它我们可以定义当列表为空时的输出内容 下面的例子与之前那个等价:
@anhuizf 可以的你构造Context的时候如下即可:c = Context({'athlete_list': [{'name': 'haha'}, {'name': 'hehe'}]})
There are no athletes. Only computer programmers. 没人看到这个吐槽吗??? “这里没有运动员,有的只是一群死宅程序员。”
在此之前我都是通过使用{% if %}...{% for %}...{% endfor %}{% else %}{% endif %}来实现这种效果的,现在有{% empty %}确实方便了许多!
{% for athlete in athlete_list %}
<p>{{ athlete.name }}</p>
{% empty %}
<p>There are no athletes. Only computer programmers.</p>
{% endfor %}
Django不支持退出循环操作。 如果我们想退出循环,可以改变正在迭代的变量,让其仅仅包含需要迭代的项目。 同理,Django也不支持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计数的。 第一次执行循环时这个变量会被设置为0。
@goofy forloop.count是以1开始计数 forloop.count0是以0开始计数 @书记 每次从0开始计算,符合程序员的逻辑,但不符合普通阅览者的逻辑。
forloop.revcounter 是表示循环中剩余项的整型变量。 在循环初次执行时 forloop.revcounter 将被设置为序列中项的总数。 最后一次循环执行中,这个变量将被置1。
forloop.revcounter0 类似于 forloop.revcounter ,但它以0做为结束索引。 在第一次执行循环时,该变量会被置为序列的项的个数减1。
forloop.first 是一个布尔值,如果该迭代是第一次执行,那么它被置为[](#id12) 在下面的情形中这个变量是很有用的:
Inline literal start-string without end-string.
{% for object in objects %}
{% if forloop.first %}<li class="first">{% else %}<li>{% endif %}
{{ object }}
</li>
{% endfor %}
是不是第一循环的 <li class='first'> {{ object }} </li> 后面的循环都是 <li> {{ object }} </li>
forloop.last 是一个布尔值;在最后一次执行循环时被置为True。 一个常见的用法是在一系列的链接之间放置管道符(|)
这里英文原版为“forloop.first is a Boolean value set to True if this is the first time through the loop. ” 所以应该是如果是第一次循环,forloop.first为true,不应该翻译为最后一次吧?
{% for link in links %}{{ link }}{% if not forloop.last %} | {% endif %}{% endfor %}
坑死了,就是个字符,自己缩进一下就明白了。 {% for link in links %} {{ link }} {% if not forloop.last %} | {% endif %} {% endfor %}
"|"只是个字符,看看下面的例子. import datetime from django.template import Template, Context raw_template = """ {% for link in links %}{{ link }}{% if not forloop.last %} # {% endif %}{% endfor %} """ t = Template(raw_template) now=datetime.datetime.now() nowtime=now.time() c = Context({ 'links': ['xx','yy','zz'] }) print t.render(c) ==> xx | yy | zz
等价于: {% for link in links %} {{ link }} {% if not forloop.last %} | #输出用|分割 {% endif %} {% endfor %}
“|”不应翻译为管道。这里就是一个竖杠字符而已。 原文:A common use for this is to put pipe characters between a list of links: 直译是管道。
上面的模板可能会产生如下的结果:
Link1 | Link2 | Link3 | Link4
另一个常见的用途是为列表的每个单词的加上逗号。
Favorite places:
{% for p in places %}{{ p }}{% 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.parentloop.counter 是正确的,他是在调用上级循环的forloop对象,然后使用该对象的counter,显示上级的循环次数。所以“<td>Country #{{ forloop.parentloop.counter }}</td>”显示的是外层for循环的循环次数,“<td>City #{{ forloop.counter }}</td>”显示的是内层for循环的循环次数。如果不明白,请自己写两个类方法测试。
forloop 变量仅仅能够在循环中使用。 在模板解析器碰到{% endfor %}标签后,forloop就不可访问了。
Context和forloop变量
在一个 {% for %} 块中,已存在的变量会被移除,以避免 forloop 变量被覆盖。 Django会把这个变量移动到 forloop.parentloop 中。通常我们不用担心这个问题,但是一旦我们在模板中定义了 forloop 这个变量(当然我们反对这样做),在 {% for %} 块中它会在 forloop.parentloop 被重新命名。
这段翻译有点奇怪。 说的是: 当模板存在一个名叫“forloop”的变量时,为了防止for块中的forloop被覆盖,将会把同名的forloop变量移至forloop.parentloop去。也就是forloop.parentloop.forloop (不知道这样说对不对)
应该不对啊,如果forloop已经被覆盖和重命名了,还怎么去访问forloop.parentloop呢? 没弄明白,求解释 他会把你定义的forloop 放到系统原始的 forloop.parentloop 这个就代表的是你的对象,forloop 仍然还是forloop
是不是说嵌套的for循环中,外层的有个forloop,内层也有个forloop。 然后为了防止覆盖, 在内层对外层forloop的引用就用forloop.parentloop来表示。 个人理解 不对请轻喷
看上面的例子,前半句话中所谓的“移除变量”,是指在第一个for循环中已存在的变量(country),该变量不受内嵌的第二个for循环的forloop变量约束,而是受第一个for循环的forloop变量(为第二个for循环的forloop变量的父类即第二个循环中使用的变量forloop.parentloop)约束;后半句话说的是,尽量不要用forloop作为模板变量名,如果使用,则该变量名将会被自动重新命名为forloop.parentloop而避免与该for循环中已经存在的forloop变量发生冲突。(话说直接禁止这样命名,抛出个错误不就得了嘛,真是的。)
有些不理解,我们在模板中定义了forloop,他会重新定义forloop以覆盖我们所定义的。然后我们定义的变量放到forloop.parentloop中,然后在系统的forloop使用过后,再将我们定义的还原回来,有点类似C++的局部变量和全局变量的关系么??是这个意思不???
貌似应该是这样的:因为你要用forloop的变量去提取数组中的数据,但是例子中有两个数组,子循环中的forloop表示的是子循环中数组的数据,但是为了要提取父循环中的数据,django把父循环中数据移到一个forloop.parentloop中以示区别。个人感觉!!
举个例子就很清楚了: >>> t = Template(''' ... {% for item in items %} ... #{{ forloop.counter }}: {{ item }} ... *{{ forloop.parentloop }} ... {% endfor %} ... ''') >>> c = Context({'items': items, 'forloop': 1000}) >>> print t.render(c) { forloop.parentloop }} 会被替换为1000
@Jasper 我感觉这个说的对,第一层的for里面的参数到了第二层for的时候 会被移除,但是我们必须调用他,所以 我们就用forloop.parentloop 来接收之前移除的变量 再来调用
我的理解是:如果你定义了名字叫forloop的变量,那么进入第一层for循环的时候,你的变量就不能再用forloop访问了,而要用forloop.parentloop访问。此时的forloop变量是for循环自带的标签模板变量。
看原文:Inside the {% for %} block, the existing variables are moved out of the way to avoid overwriting the magic forloop variable. Django exposes this moved context in forloop.parentloop. You generally don’t need to worry about this, but if you supply a template variable named forloop (though we advise against it), it will be named forloop.parentloop while inside the {% for %} block.
意思就是如果你自己定义了forloop变量,那么在for循环中的部分会被循环自带的forloop变量覆盖,在for循环外的部分你仍可以使用你自己定义的forloop。如果你一定要在for循环内部使用自己定义的forloop的话那么在内部你应该用forloop.parentloop表示你自己定义的那个变量。
不不不,我理解的意思是,不是定义forloop这个变量。是指代码中有一个变量a,但是在for循环中也定义了一个块作用域的变量a(比如:{% for a in a_list %} ...{% endfor %}里面),那么你在这个for循环里面调用外层的变量a,就要用 forloop.parentloop.a 去调用。 但是你如果在代码中定义了一个forloop变量的话,for循环中的forloop.parentloop就被相应地重写了。因此不要随便瞎创建叫做forloop变量。
给大家举个例子 {# 手欠,自己先造了一个变量 #} {% with forloop='我才是正宗的forloop' %} 外层前:{{ forloop }} {# 预期:输出“我才是正宗的forloop” #} {% for item in items %} 循环里:{{ forloop }} {# Django 自带的循环对象 #} 循环里parentloop:{{ forloop.parentloop }} {# 被挪走的“正版” #} {% endfor %} 外层后:{{ forloop }} {# 又恢复成“我才是正宗的forloop” #} {% endwith %}
Django模板系统压根儿就没想过实现一个全功能的编程语言,所以它不允许我们在模板中执行Python的语句(还是那句话,要了解更多请参看理念和限制小节)。 但是比较两个变量的值并且显示一些结果实在是个太常见的需求了,所以Django提供了 {% ifequal %} 标签供我们使用。
使用硬编码字符的时候需要注意如下: t = Template("""{% ifequal a "b" %} Welcome! {% endifequal%}""") 如果使用的不是三引号,硬编码字符应该与外面的引号相反
这个标签已经过时了,现在直接用比较符号 There’s really no reason to use {% ifequal %} or {% ifnotequal %} anymore, unless you’re the nostalgic type. The operators supported are ==, !=, <, >, <=, >=, in and not in, all of which work like the Python operators, in addition to and, or and not, which were already supported.
{% ifequal %} 标签比较两个值,当他们相等时,显示在 {% ifequal %} 和 {% endifequal %} 之中所有的值。
下面的例子比较两个模板变量 user 和 currentuser :
{% ifequal user currentuser %}
<h1>Welcome!</h1>
{% endifequal %}
参数可以是硬编码的字符串,随便用单引号或者双引号引起来,所以下列代码都是正确的:
{% ifequal section 'sitenews' %}
<h1>Site News</h1>
{% endifequal %}
{% ifequal section "community" %}
<h1>Community</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" %}
其他任何类型,例如Python的字典类型、列表类型、布尔类型,不能用在 {% ifequal %} 中。 下面是些错误的例子:
{% ifequal variable True %}
{% ifequal variable [1, 2, 3] %}
{% ifequal variable {'key': 'value'} %}
如果你需要判断变量是真还是假,请使用 {% if %} 来替代 {% ifequal %} 。
就像HTML或者Python,Django模板语言同样提供代码注释。 注释使用 {# #} :
{# This is a comment #}
注释的内容不会在模板渲染时输出。
用这种语法的注释不能跨越多行。 这个限制是为了提高模板解析的性能。 在下面这个模板中,输出结果和模板本身是 完全一样的(也就是说,注释标签并没有被解析为注释):
This is a {# this is not
a comment #}
test.
如果要实现多行注释,可以使用{% comment %} 模板标签,就像这样:
{% comment %}
This is a
multi-line comment.
{% endcomment %}
就象本章前面提到的一样,模板过滤器是在变量被显示前修改它的值的一个简单方法。 过滤器使用管道字符,如下所示:
{{ name|lower }}
显示的内容是变量 {{ name }} 被过滤器 lower 处理后的结果,它功能是转换文本为小写。
过滤管道可以被 套接 ,既是说,一个过滤器管道的输出又可以作为下一个管道的输入,如此下去。 下面的例子实现查找列表的第一个元素并将其转化为大写。
{{ my_list|first|upper }}
有些过滤器有参数。 过滤器的参数跟随冒号之后并且总是以双引号包含。 例如:
{{ bio|truncatewords:"30" }}
这个将显示变量 bio 的前30个词。
以下几个是最为重要的过滤器的一部分。 附录F包含其余的过滤器。
addslashes : 添加反斜杠到任何反斜杠、单引号或者双引号前面。 这在处理包含JavaScript的文本时是非常有用的。
date : 按指定的格式字符串参数格式化 date 或者 datetime 对象, 范例:
{{ pub_date|date:"F j, Y" }}
格式参数的定义在附录F中。
length : 返回变量的长度。 对于列表,这个参数将返回列表元素的个数。 对于字符串,这个参数将返回字符串中字符的个数。 你可以对列表或者字符串,或者任何知道怎么测定长度的Python 对象使用这个方法(也就是说,有 __len__() 方法的对象)。
现在你已经对Django的模板语言有一些认识了,我们将指出一些特意设置的限制和为什么要这样做 背后的一些设计哲学。
相对与其他的网络应用的组件,模板的语法很具主观性,因此可供程序员的选择方案也很广泛。 事实上,Python有成十上百的 开放源码的模板语言实现。 每个实现都是因为开发者认为现存的模板语言不够用。 (事实上,对一个 Python开发者来说,写一个自己的模板语言就象是某种“成人礼”一样! 如果你还没有完成一个自己的 模板语言,好好考虑写一个,这是一个非常有趣的锻炼。 )
"More than any other component of Web applications": 和其它web 应用的组件比起来 "and programmers’ opinions vary wildly", 而且程序员们的各种观点也大相径庭, 同时我觉得and 似乎不翻出来也行
明白了这个,你也许有兴趣知道事实上Django并不强制要求你必须使用它的模板语言。 因为Django 虽然被设计成一个FULL-Stack的Web框架,它提供了开发者所必需的所有组件,而且在大多数情况 使用Django模板系统会比其他的Python模板库要 更方便 一点,但是并不是严格要求你必须使用 它。 你将在后续的“视图中应用模板”这一章节中看到,你还可以非常容易地在Django中使用其他的模板语言。
虽然如此,很明显,我们对Django模板语言的工作方式有着强烈的偏爱。 这个模板语言来源于World Online的开发经验和Django创造者们集体智慧的结晶。 下面是关于它的一些设计哲学理念:
业务逻辑应该和表现逻辑相对分开 。我们将模板系统视为控制表现及表现相关逻辑的工具,仅此而已。 模板系统不应提供超出此基本目标的功能。
出于这个原因,在 Django 模板中是不可能直接调用 Python 代码的。 所有的编程工作基本上都被局限于模板标签的能力范围。 当然, 是 有可能写出自定义的模板标签来完成任意工作,但这些“超范围”的 Django 模板标签有意地不允许执行任何 Python 代码。
业务逻辑跟表现逻辑分开;开发的过程中,有时候觉得python不够强大,不能再模板语言中直接调用python语言,限制太多,这时候就想着各种改,希望可以这样,又可以那样,最终搞得脱离python的设计理念,然后又回到了原点,白费一番功夫
语法不应受到 HTML/XML 的束缚 。尽管 Django 模板系统主要用于生成 HTML,它还是被有意地设计为可生成非 HTML 格式,如纯文本。 一些其它的模板语言是基于 XML 的,将所有的模板逻辑置于 XML 标签与属性之中,而 Django 有意地避开了这种限制。 强制要求使用有效 XML 编写模板将会引发大量的人为错误和难以理解的错误信息,而且使用 XML 引擎解析模板也会导致令人无法容忍的模板处理开销。
假定设计师精通 HTML 编码 。模板系统的设计意图并不是为了让模板一定能够很好地显示在 Dreamweaver 这样的所见即所得编辑器中。 这种限制过于苛刻,而且会使得语法不能像目前这样的完美。 Django 要求模板创作人员对直接编辑 HTML 非常熟悉。
假定设计师不是 Python 程序员 。模板系统开发人员认为:模板通常由设计师而非程序员来编写,因此不应被假定拥有Python开发知识。
当然,系统同样也特意地提供了对那些 由 Python 程序员进行模板制作的小型团队的支持。 它提供了一种工作模式,允许通过编写原生 Python 代码进行系统语法拓展。 (详见第十章)
目标并不是要发明一种编程语言 。目标是恰到好处地提供如分支和循环这一类编程式功能,这是进行与表现相关判断的基础。
在学习了模板系统的基础之后,现在让我们使用相关知识来创建视图。 重新打开我们在前一章在 mysite.views 中创建的 current_datetime 视图。 以下是其内容:
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
让我们用 Django 模板系统来修改该视图。 第一步,你可能已经想到了要做下面这样的修改:
from django.template import Template, Context
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
t = Template("<html><body>It is now {{ current_date }}.</body></html>")
html = t.render(Context({'current_date': now}))
return HttpResponse(html)
没错,它确实使用了模板系统,但是并没有解决我们在本章开头所指出的问题。 也就是说,模板仍然嵌入在Python代码里,并未真正的实现数据与表现的分离。 让我们将模板置于一个 单独的文件 中,并且让视图加载该文件来解决此问题。
你可能首先考虑把模板保存在文件系统的某个位置并用 Python 内建的文件操作函数来读取文件内容。 假设文件保存在 /home/djangouser/templates/mytemplate.html 中的话,代码就会像下面这样:
from django.template import Template, Context
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
# Simple way of using templates from the filesystem.
# This is BAD because it doesn't account for missing files!
fp = open('/home/djangouser/templates/mytemplate.html')
t = Template(fp.read())
fp.close()
html = t.render(Context({'current_date': now}))
return HttpResponse(html)
TypeError: context must be a dict rather than Context. 将html = t.render(Context({'current_date':now})) 改为html = t.render({'current_date':now})即可
@Adoo 不用这么麻烦吧,直接转义就行啦。例: with open(r'D:\demo\mytemplate.html') as f: content = f.read()
然而,基于以下几个原因,该方法还算不上简洁:
- 它没有对文件丢失的情况做出处理。 如果文件
mytemplate.html不存在或者不可读,open()函数调用将会引发IOError异常。 - 这里对模板文件的位置进行了硬编码。 如果你在每个视图函数都用该技术,就要不断复制这些模板的位置。 更不用说还要带来大量的输入工作!
- 它包含了大量令人生厌的重复代码。 与其在每次加载模板时都调用
open()、fp.read()和fp.close(),还不如做出更佳选择。
为了解决这些问题,我们采用了 模板自加载 跟 模板目录 的技巧.
模板加载
谷歌搜的,加入下边代码到settings.py中,同时我是在manage.py同一级目录创建的templates文件夹: from os.path import join TEMPLATE_DIRS = (join(BASE_DIR, 'templates'), )
为了减少模板加载调用过程及模板本身的冗余代码,Django 提供了一种使用方便且功能强大的 API ,用于从磁盘中加载模板,
要使用此模板加载API,首先你必须将模板的保存位置告诉框架。 设置的保存文件就是我们前一章节讲述ROOT_URLCONF配置的时候提到的 settings.py。
如果你是一步步跟随我们学习过来的,马上打开你的settings.py配置文件,找到TEMPLATE_DIRS这项设置吧。 它的默认设置是一个空元组(tuple),加上一些自动生成的注释。
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
)
1.5里面是那样设置的,但1.6默认是不需要设置的,默认的寻找路径是在app下面新建一个templates目录,所有的模板放进去,系统会自己找到的~ 从知乎里找到的。。
在BASE_DIR下面加入TEMPLATE_DIRS = ( os.path.join(BASE_DIR, 'templates').replace('\\','/'), )就可以了
在TEMPLATE的DIRS中添加模板目录即可,例如: TEMPLATES = [ { ...... 'DIRS': [ os.path.join(BASE_DIR, 'templates'), ], ...... }, ]
1.8的文档里面是这么描述的: A new setting was introduced in Django 1.8: TEMPLATES. All existing template-related settings were deprecated. During the deprecation period, Django will create a backwards-compatible TEMPLATES based on the TEMPLATE_* settings if you don’t define it yourself.
我的是1.8版本,在下面这段里加入“mysite”,然后django会自动在mysite里找到templates文件夹的。记住,templates文件夹放在mysite文件夹下面: INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'mysite', )
1.8官方文档上是在TEMPLATES = [ ]里面 'DIRS': [ ]那里添加的。。。。。。 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ # insert your TEMPLATE_DIRS here ], 'APP_DIRS': True,
django1.9没有TEMPLATES_DIR的情况,在setting.py中TEMPLATES下有个空DIR,里面填写os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),亲测可用
django1.9没有TEMPLATES_DIR的情况,在setting.py中TEMPLATES下有个空DIR,里面填写os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),亲测可用。另,模板文件夹templates在setting.py同级目录下
新版本的Django中没有这个设置选项而是改为TEMPLATES这选项下有‘DIRS’一项为一个列表,默认里面为空,会加载app下templates文件夹中的模板。可以在这个选项列表中自行加入指定的模板路径。
该设置告诉 Django 的模板加载机制在哪里查找模板。 选择一个目录用于存放模板并将其添加到 TEMPLATE_DIRS 中:
TEMPLATE_DIRS = (
'/home/django/mysite/templates',
)
TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')] https://docs.djangoproject.com/en/1.6/intro/tutorial02/
Django version 1.6。默认到app列表制定的目录查找templates目录,在该目录中,找到第一个与视图函数中些的目标模板,找到后渲染页面。所以关键在于,需要将你的app,添加到app列表中。那么如何添加?settings.py文件中:INSTALLED_APPS项目,大概在35行。INSTALLED_APPS = ( # 注意:去掉所有中文注释 'mysite',# 这里本来不包含mysite,如果还有其他的也加进来。 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.admin', #'bg', # add others app )
Django version 1.6。默认到app列表制定的目录查找templates目录,在该目录中,找到第一个与视图函数中些的目标模板,找到后渲染页面。所以关键在于,需要将你的app,添加到app列表中。那么如何添加?settings.py文件中:INSTALLED_APPS项目,大概在35行。 # 注意:去掉所有中文注释。 # 这里本来不包含mysite,如果还有其他的也加进来。 INSTALLED_APPS = ( 'mysite', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.admin', #'bg', # add others app )
加这句:TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')] 和在这里INSTALLED_APPS = ( ......加'mysite'.
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', ], }, }, ] 1.8 https://docs.djangoproject.com/en/1.8/intro/tutorial02/
下面是一些注意事项:
你可以任意指定想要的目录,只要运行 Web 服务器的用户可以读取该目录的子目录和模板文件。 如果实在想不出合适的位置来放置模板,我们建议在 Django 项目中创建一个 templates 目录(也就是说,如果你一直都按本书的范例操作的话,在第二章创建的 mysite 目录中)。
这里应该说明的一点是必须是绝对路径,而不是相对路径。我有点疑问,在新浪的SAE中这个地方时如何使用的? Don't forget to use absolute paths, not relative paths.
既然要用os.path.join(BASE_DIR,'templates'),那么,templates目录是要创建在哪里??? 项目叫mysite,但是项目目录中还有一个mytest目录,我是Django 1.6.5
如果你的 TEMPLATE_DIRS只包含一个目录,别忘了在该目录后加上个逗号。
Bad:
# Missing comma!
TEMPLATE_DIRS = (
'/home/django/mysite/templates'
)
Deprecated since version 1.8: Set the DIRS option of a DjangoTemplates backend instead. # 1.8 以后的版本,使用 DIRS 这个环境变量来设 source: https://docs.djangoproject.com/en/1.8/ref/settings/
Good:
# Comma correctly in place.
TEMPLATE_DIRS = (
'/home/django/mysite/templates',
)
Python 要求单元素元组中必须使用逗号,以此消除与圆括号表达式之间的歧义。 这是新手常犯的错误。
如果使用的是 Windows 平台,请包含驱动器符号并使用Unix风格的斜杠(/)而不是反斜杠(),就像下面这样:
TEMPLATE_DIRS = (
'C:/www/django/templates',
)
相信大家都是这么写的吧 os.path.join(BASE_DIR, 'templates').replace('\\', '/'), 大家肯定将templates这个文件夹放到mysite 这个目录下了。 BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 他获得的地址是当前这个模块的路径,所以路径中少了个mysite ,导致找不到这个模板文件
最省事的方式是使用绝对路径(即从文件系统根目录开始的目录路径)。 如果想要更灵活一点并减少一些负面干扰,可利用 Django 配置文件就是 Python 代码这一点来动态构建 TEMPLATE_DIRS 的内容,如: 例如:
建议:“可利用 Django 配置文件就是 Python 代码这一点来动态构建 TEMPLATE_DIRS 的内容”改为“可利用Python 代码在Django 配置文件中动态构建 TEMPLATE_DIRS 的内容”
import os.path
TEMPLATE_DIRS = (
os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),
)
我是在win2008下部署的环境,ECLIPSE + PYDEV 书中提及的对模板目录的配置对我的环境无效, 必须改成: TEMPLATE_DIRS = ( ''.join([os.path.dirname(__file__),'/Template/TEMP']).replace('\\','/'), )
`` os.path.dirname(__file__)`` 将会获取"(__file__)"文件所在的目录,即settings.py 所在的目录(本例中为mysite)。然后由os.path.join这个方法将此目录与templates目录进行连接,linux下形成如mysite/templates;windows下形成如mysite\templates。
TEMPLATE_DIRS=( os.path.join(os.path.dirname(__file__),'../templates').replace('\\','/'), ) 这样就可以把templates文件夹放在上一层目录了,如果需要,可以放在其他目录,但是要修改'../templates'
Django_1.7版本亲测,templates文件放在和settings文件同一个目录下,settings文件中的BASE_DIR语句不要修改它,因为settings文件下面的语句会用到它。在下面自己添加一句TEMPLATE_DIRS=(os.path.join(os.path.dirname(__file__),'templates').replace('\\','/'),就可以成功寻找到模版。另外有同学把templates文件创建在和manage.py同一个目录下的话,那么TEMPLATE_DIR语句就要多添加一个函数os.path.dirname(),因为这个函数就是显示自身文件的目录地址的,在使用多一次这个函数,就可以把地址修改到原来目录的上一层。
出现错误 NameError: name '__file__' is not defined 改用下面的方法 os.path.join(os.path.dirname(os.path.realpath('__file__')), 'templates').replace('\\','/')
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/')], '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', ], }, }, ] django 1.9版本,直接在TEMPLATE下面的DIRS添加模板路径就可以了,亲测可用
这个例子使用了神奇的 Python 内部变量 __file__ ,该变量被自动设置为代码所在的 Python 模块文件名。 os.path.dirname(__file__) 将会获取自身所在的文件,即settings.py 所在的目录,然后由os.path.join 这个方法将这目录与 templates 进行连接。如果在windows下,它会智能地选择正确的后向斜杠”“进行连接,而不是前向斜杠”/”。
`` os.path.dirname(__file__)`` 将会获取"(__file__)"文件所在的目录,即settings.py 所在的目录(本例中为mysite)。然后由os.path.join这个方法将此目录与templates目录进行连接,linux下形成如mysite/templates;windows下形成如mysite\templates。
在这里我们面对的是动态语言python代码,我需要提醒你的是,不要在你的设置文件里写入错误的代码,这很重要。 如果你在这里引入了语法错误,或运行错误,你的Django-powered站点将很可能就要被崩溃掉。
完成 TEMPLATE_DIRS 设置后,下一步就是修改视图代码,让它使用 Django 模板加载功能而不是对模板路径硬编码。 返回 current_datetime 视图,进行如下修改:
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('current_datetime.html')
html = t.render(Context({'current_date': now}))
return HttpResponse(html)
最好能描述一下,测试此例子时,需要模板目录里新建一个文件, 文件名为:current_datetime.html。文件内容为: <html><body>It is now {{ current_date }}.</body></html>
为什么像下面这样设置全路径报错说找不到模板文件呢? t = get_template('C:/Inetpub/wwwroot/django/mysite/mysite/template/current_datetime2.html')
为什么像下面这样设置全路径报错说找不到模板文件呢? t = get_template('C:/Inetpub/wwwroot/django/mysite/mysite/template/current_datetime2.html') 直接在浏览器中打入该文件地址显示文件存在啊,地址路径和文件名都没错误
这个太让我崩溃了 os.path.join(BASE_DIR, 'templates').replace('\\', '/'), 设置的好好的,怎么就报错呢
django 1.6.2 如下设置,正常 TEMPLATE_DIRS = ( (BASE_DIR+'/templates').replace('\\','/'), ) http://stackoverflow.com/questions/17168256/template-does-not-exist
django 1.6.2<br/> 如下设置,正常。<br/> TEMPLATE_DIRS = ( (BASE_DIR+'/templates').replace('\\','/'), )<br/> http://stackoverflow.com/questions/17168256/template-does-not-exist
1.6下到django/conf/global_settings.py里面设置TEMPLATE_DIRS = ('F:/pythonDjango/mysite/mysite/templates',)
设置template目录方法: 1、在manage.py同级目录下新建文件夹 ,名为template 2、修改setting.py: import os.path Temp_Path = os.path.realpath('.') BASE_DIR = os.path.dirname(os.path.dirname(__file__)) STATIC_ROOT = '' STATIC_URL = '/static/' TEMPLATE_DIRS = ( Temp_Path +"/template" )
把templates文件夹建在和manage.py同级的文件夹里,而不是settings, 因为settings是在mysite/mysite文件夹里,它寻找模板是在mysite/templates而不是mysite/mysite/templates
我用的django1.8版本,出现无法找到template下面模版文件 setting.py文件配置 import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) TEMPLATE_DIRS = ( os.path.join(BASE_DIR, 'templates'), ) 然后是views.py文件配置 from django.http import HttpResponse from django.template import Context import datetime from django.template.loader import get_template def current_datetime(request): now = datetime.datetime.now() t = get_template('current_datetime.html') html = t.render(Context({'current_date':now})) return HttpResponse(html) urls.py配置 url(r'^time/$', current_datetime), 错误提示: TemplateDoesNotExist at /time/ current_datetime.html Request Method: GET Request URL: http://127.0.0.1:8000/time/ Django Version: 1.8 Exception Type: TemplateDoesNotExist Exception Value: current_datetime.html 没找出问题,求大神帮助!谢谢
django1.8里面需要将模板的路径加到setting.py中的TEMPLATES里的DIRS,像这样子:TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ os.path.join(BASE_DIR, 'templates').replace('\\', '/'), ], ......(省略后面的内容了.)
要在创建的template文件里创建一个current_datetime.html,不然的话在浏览器中输入url后将会出现TemplateDoesNotExist的页面错误。错误指向current_datetime
from mysite.views import hello,hours_ahead,current_datetime 这样子写没有错吧,为什么刚开始好使 之后不知道做了什么,就开始报错了:ImportError: cannot import name current_datetime 这个事为什么
html=r.render(Context({'Currnet_date':now})) 此行出错 context must be a dict rather than Context. 用的django 1.11.2版本,get_template貌似弃用Context()了
用的最新版本django 1.11 执行时候报错: context must be a dict rather than Context. 将代码html = t.render(Context({'current_date': now}))改为:html = t.render({'current_date': now})即可,去掉Context()
执行时候报错: context must be a dict rather than Context. 将代码html = t.render(Context({'current_date': now}))改为:html = t.render({'current_date': now})即可,去掉Context()
@lvze正解,1.11的版本,render接收的参数是字典,不需要借助Context了,也即: now=datetime.datetime.now() t=get_template('curtime.html') d={'cur_time':str(now)} html=t.render(d)#不再是render(Context(d)) return HttpResponse(html)
报错:context must be a dict rather than Context. 解决: t = get_template("index.html") d = {"name": "Jack", "items":["item1", "item2", "item3"]} html=t.render(d) return HttpResponse(html) 说明:render一个字典即可,不用传Context对象
django2.0 templates文件夹和mysite在同一级 'DIRS': [os.path.join(BASE_DIR,'templates')] 成功
楼上正解!!!1.11的版本,render接收的参数是字典,不需要借助Context了。 html = t.render(Context({'current_date': now}))去掉Context()就可以解决type error:context must be a dict rather than Context.
django2.2 templates文件夹和manage.py在同一级 'DIRS': [os.path.join(BASE_DIR,'templates')] 亲测可以
django1.8 python2.7 亲测,2个需要修改: 1. TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'mysite/templates').replace('\\', '/'),],...}] 2. c 直接写成字典形式,不用context
django 3.0版本后, t = get_template('mytemplate.html') # c = Context({'current_date': now}) html = t.render({'current_date': now})
django 3.0版本后, t = get_template('mytemplate.html') html = t.render({'current_date': now})
此范例中,我们使用了函数 django.template.loader.get_template() ,而不是手动从文件系统加载模板。 该 get_template() 函数以模板名称为参数,在文件系统中找出模块的位置,打开文件并返回一个编译好的 Template 对象。
在这个例子里,我们选择的模板文件是current_datetime.html,但这个与.html后缀没有直接的联系。 你可以选择任意后缀的任意文件,只要是符合逻辑的都行。甚至选择没有后缀的文件也不会有问题。
可以在创建这个模板的时候不指定后缀名,或者随便指定一个后缀名,但是在传给get_template这个方法的时候,一定是硬盘上是什么就传什么,硬盘上带后缀的就一定要写对后缀。否则还是报找不到的错误。
要确定某个模板文件在你的系统里的位置, get_template()方法会自动为你连接已经设置的 TEMPLATE_DIRS目录和你传入该法的模板名称参数。比如,你的 TEMPLATE_DIRS目录设置为 '/home/django/mysite/templates',上面的 get_template()调用就会为你找到 /home/django/mysite/templates/current_datetime.html 这样一个位置。
如果 get_template() 找不到给定名称的模板,将会引发一个 TemplateDoesNotExist 异常。 要了解究竟会发生什么,让我们按照第三章内容,在 Django 项目目录中运行 python manage.py runserver 命令,再次启动Django开发服务器。 接着,告诉你的浏览器,使其定位到指定页面以激活current_datetime视图(如 http://127.0.0.1:8000/time/ )。假设你的 DEBUG项设置为 True,而你有没有建立current_datetime.html 这个模板文件,你会看到Django的错误提示网页,告诉你发生了 TemplateDoesNotExist 错误。
搞了半天,终于没错了,templates文件夹要在manage.py同一级,我误会了os.path.dirname(os.path.dirname(__file__))的意思
1.8在 TEMPLATES = [ { 'DIRS': [ os.path.join(BASE_DIR, 'templates'), ], ***** 这个位置提交就可以找到模板了 不用再另外写了
图 4-1: 模板文件无法找到时,将会发送提示错误的网页给用户。
该页面与我们在第三章解释过的错误页面相似,只不过多了一块调试信息区: 模板加载器事后检查区。 该区域显示 Django 要加载哪个模板、每次尝试出错的原因(如:文件不存在等)。 当你尝试调试模板加载错误时,这些信息会非常有帮助。
出现一个问题, t = get_template('current_datetime.html'),会出现上述错误TemplateDoesNotExist ,但是当 t = get_template('current_datetime.html '),注意看,html后有一个空格,然后网页能正常显示了,这是怎么回事呢?
django 1.7中添加一句“TEMPLATE_DIRS=[os.path.join(BASE_DIR,'template'),]”就可以正常加载模板了,记住templates文件夹与manage.py同级
请教大家应该怎么在网页中显示图片呢?我在current_datetime.html文件中加入html的语句<img src="here is photo's path">网页中加载的图是挂掉的。。
接下来,在模板目录中创建包括以下模板代码 current_datetime.html 文件:
<html><body>It is now {{ current_date }}.</body></html>
在网页浏览器中刷新该页,你将会看到完整解析后的页面。
我采用硬编码方法和采用模板方式显示的页面时间格式不一样。 time now is Feb. 5, 2012, 2:59 p.m. 这是采用模板方式显示的时间格式 time is now 2012-02-05 15:02:44.142162 这是采用硬编码方式显示的时间格式。 用模板的时候这中间转换有什么不用吗?
在这句话t = get_template('current_datetime.html ') 里有UnicodeDecodeError怎么办?在mysite里多了wsgi.py这个文件是因为安装了什么数据库么???求指点
在templates文件下按照要求做了 current_datetime.html 文件,刷新http://127.0.0.1:8000/time,还是显示错误: TemplateDoesNotExist at /time/ current_datetime.htmlRequest Method: GET Request URL: http://127.0.0.1:8000/time/ Django Version: 1.6.5 Exception Type: TemplateDoesNotExist Exception Value: current_datetime.html
这里需要注意一下,我用Django 1.8的,按照教程会出现TemplateDoesNotExist异常,是因为settings.py文件还有一个地方需要修改路径地址。TEMPLATE里面的DIRS,理论上填上os.path.join(BASE_DIR, 'templates')可以了(和最下面的TEMPLATE_DIRS一样。)
一样的步骤下来却还是出错说找不到资源,为什么 Template-loader postmortem Django tried loading these templates, in this order: Using engine django: django.template.loaders.filesystem.Loader: /home/bytetech/mysite/templates/current_datetime (Source does not exist) django.template.loaders.app_directories.Loader: /usr/lib/python2.7/site-packages/Django-1.9.1-py2.7.egg/django/contrib/admin/templates/current_datetime (Source does not exist) django.template.loaders.app_directories.Loader: /usr/lib/python2.7/site-packages/Django-1.9.1-py2.7.egg/django/contrib/auth/templates/current_datetime (Source does not exist)
render_to_response()
我们已经告诉你如何载入一个模板文件,然后用 Context渲染它,最后返回这个处理好的HttpResponse对象给用户。 我们已经优化了方案,使用 get_template() 方法代替繁杂的用代码来处理模板及其路径的工作。 但这仍然需要一定量的时间来敲出这些简化的代码。 这是一个普遍存在的重复苦力劳动。Django为此提供了一个捷径,让你一次性地载入某个模板文件,渲染它,然后将此作为 HttpResponse返回。
该捷径就是位于 django.shortcuts 模块中名为 render_to_response() 的函数。大多数情况下,你会使用](#id19)`\ ```[``对象,除非你的老板以代码行数来衡量你的工作。
Inline literal start-string without end-string.
Inline literal start-string without end-string.
Inline literal start-string without end-string.
大多数情况下,你会使用render_to_response()而不是手动加载模板并创建Context和HttpResponse对象,除非你的老板以代码行数来衡量你的工作
我觉得是这样的:翻译呢自然是问题一大堆,而且我看很多反馈了很久都没有得到修改的。但是博主是画了大量时间、精力和金钱来做这件事的,而且是无偿给大家阅读,就凭这你就不能随意骂别人的成果是垃圾。
一个人无偿做这样一个工作来使得我们获益 本就很好了 而且 翻译这么多内容 肯定不免有一些纰漏 这些纰漏也通过我们大家的沟通得到了解决 能更加深我们的理解和印象 希望不喜勿喷
为啥网页显示 System Message: WARNING/2 (<string>, line 1736); backlink Inline literal start-string without end-string. System Message: WARNING/2 (<string>, line 1736); backlink Inline literal start-string without end-string. System Message: WARNING/2 (<string>, line 1736); backlink Inline literal start-string without end-string.
“免费就是不好好干的借口么?”这么浩大无私的翻译工程,你有什么资格一句话说就能说别人不好好干,一句话否定别人这么无私的奉献?你是出资人还是那根葱?就问一句你有什么资格?“先不说把阅读者吸引过来结果没学成,浪费了阅读者的时间;单说就这种质量,对得起自己花费的时间吗?对得起社区的名声吗?”别让你的自私自利污染了所有阅读者,你只是代表你,“没学成,浪费时间”一说也只是代表你自己,没人逼着你“浪费时间”。要是真的评判责任,那也只是你自己对自己负责。另外,社区的名声也不是你能否定的,这么多读者的热情支持也不是你一句话能否定的。不想说什么,自己学会对自己负责,没人能替你。
这么好的资源,网页连广告都没有,不知道某些喷子还有什么不满意的,作者也可以做,也可以不做,人家做了,给大家这么好的学习机会 还有什么不满足的 不要每天只是喷喷喷
下面就是使用 render_to_response() 重新编写过的 current_datetime 范例。
from django.shortcuts import render_to_response
import datetime
def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('current_datetime.html', {'current_date': now})
to goofy: 这两者应该是一样的. 实例化一个class 不就是调用class的构造方法来生成一个object作为返回值 i = int("3") i = myself_to_int("3") 这两种本质应该是一样的, 只是前者有语法糖(i = int("3")) 等同于(只是猜想,对于python也是刚接触) result = {} int.__init__(result, "3") return result 如果非要区分的话就要看f(args) 中的f是什么东西了 是函数就是函数调用, 是类名就是实例化 通过 f.__class__的内容或者通过hasattr来检测是否有f.__init__这个方法 具体情况具体看待吧
"current_date 应该为 current_time" 我看错了,就是current_date, 上面的html中已经为current_date
同步实践时应注意:将current_datetime.html中的{{current_date}}改为{{current_datetime}}。文中前后不一致。
原文的做法是 return render(request, 'dateapp/current_datetime.html', {'current_date': now}) 同樣也可,不知哪種做法較好?
from django.shortcuts import render \n<br /> return render(request, 'current_datetime.html', {'current_date': now})现在这样写
http://www.douban.com/note/278152737/ http://stackoverflow.com/questions/5154358/django-what-is-the-difference-between-render-render-to-response-and-direc
render() Required arguments 是 request 和 template_name 有两个,而render_to_response() Required arguments 只有一个是 template_name,后者更少写起来不是更方便吗?
新版使用render即可. from django.shortcuts import render import datetime def current_datetime(request): now = datetime.datetime.now() return render(request, 'current_datetime.html', {'current_date': now})
直接return render(request, 'current_datetime.html', {'current_date': now}) 就行啦(Django2.1.7测试有效)
def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] context = {'latest_question_list': latest_question_list} return render(request, 'polls/index.html', context)
大变样了! 让我们逐句看看代码发生的变化:
- 我们不再需要导入
get_template、Template、Context和HttpResponse。相反,我们导入django.shortcuts.render_to_response。import datetime继续保留. - 在
current_datetime函数中,我们仍然进行now计算,但模板加载、上下文创建、模板解析和HttpResponse创建工作均在对render_to_response()的调用中完成了。 由于render_to_response()返回HttpResponse对象,因此我们仅需在视图中return该值。
render_to_response() 的第一个参数必须是要使用的模板名称。 如果要给定第二个参数,那么该参数必须是为该模板创建 Context 时所使用的字典。 如果不提供第二个参数, render_to_response() 使用一个空字典。
英文版更新到Django 1.4, 1.5 用render()函数 http://www.djangobook.com/en/2.0/chapter04.html
render_to_response()函数下岗了,进化为render(request,'templatename',Context)函数!一句话解决问题!!!
django1.11版本里可以用render() from django.shortcuts import render # # return render(request,'template名字',{'current_date':str(now)}) request不可以缺 context接收字典
locals() 技巧
用locals不是好习惯,如果本地变量过多会造成你以后修改代码不知道模板中用了变量,如果变量过少也自然没有必要用locals,想来想去使用场景可能只有当view方法是一个动态方法时候
思考一下我们对 current_datetime 的最后一次赋值:
def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('current_datetime.html', {'current_date': now})
很多时候,就像在这个范例中那样,你发现自己一直在计算某个变量,保存结果到变量中(比如前面代码中的 now ),然后将这些变量发送给模板。 尤其喜欢偷懒的程序员应该注意到了,不断地为临时变量和临时模板命名有那么一点点多余。 不仅多余,而且需要额外的输入。
如果你是个喜欢偷懒的程序员并想让代码看起来更加简明,可以利用 Python 的内建函数 locals() 。它返回的字典对所有局部变量的名称与值进行映射。 因此,前面的视图可以重写成下面这个样子:
在这里的时候,用locals要写一下对应的在html里面的读取方式,不然这么写是错误的。我是完全的新手,只会按照例子一步一步来。如果替换了这段代码,那么实际上运行起来就是错误的。
def current_datetime(request):
current_date = datetime.datetime.now()
return render_to_response('current_datetime.html', locals())
在此,我们没有像之前那样手工指定 context 字典,而是传入了 locals() 的值,它囊括了函数执行到该时间点时所定义的一切变量。 因此,我们将 now 变量重命名为 current_date ,因为那才是模板所预期的变量名称。 在本例中, locals() 并没有带来多 大 的改进,但是如果有多个模板变量要界定而你又想偷懒,这种技术可以减少一些键盘输入。
views.py: from django.shortcuts import render_to_response import datetime def current_datetime(request): now1 = datetime.datetime.now() system="keyman.example.com" return render_to_response('index.html',locals()) index.html: <html> <body> <h1> {{system}}'s time is now {{now1}}.</h1> </body> </html>
个人觉得locals应该蛮好用的,只不过在模板里的变量的名字要和你在views中定义的一样,这样在你用render_to_response时,就不会要在末尾写一长串的数据了
这里并没有违背模板和代码分离,因为即使你不用locals(),你也要写 {'current_date': now},这里的current_date也要和模板里的变量名匹配
对于n多变量这个应该非常好使,而且省事,对于少量变量可能效率不会太高吧,因为locals()本身还包括很多其他变量 还有一点就是 locals()只能用于1对1吧 如果多对一的情况 就不好办 不知道实际中有么有这种情况 对于一对多我想应该是不会 有对吧^ ^
写代码最好要让人直接看懂,而不是二次理解,花时间去弄清楚你表达的意思。 ```# 比如使用工厂函数 d = dict( name='wang', sale='man', ) render_to_response('current_datetime.html', d) ```
使用locals()没法传参耶。。--》错因找到了,因为变量没有匹配上。。--》 如果要使用locals(),就要保证 视图层函数里 定义的变量名与模板里相应的变量名一致,才能成功传参。 如果用视图层用字典传参,则不要求名字一致。
使用 locals() 时要注意是它将包括 所有 的局部变量,它们可能比你想让模板访问的要多。 在前例中, locals() 还包含了 request 。对此如何取舍取决你的应用程序。
get_template()中使用子目录
把所有的模板都存放在一个目录下可能会让事情变得难以掌控。 你可能会考虑把模板存放在你模板目录的子目录中,这非常好。 事实上,我们推荐这样做;一些Django的高级特性(例如将在第十一章讲到的通用视图系统)的缺省约定就是期望使用这种模板布局。
把模板存放于模板目录的子目录中是件很轻松的事情。 只需在调用 get_template() 时,把子目录名和一条斜杠添加到模板名称之前,如:
t = get_template('dateapp/current_datetime.html')
由于 render_to_response() 只是对 get_template() 的简单封装, 你可以对 render_to_response() 的第一个参数做相同处理。
return render_to_response('dateapp/current_datetime.html', {'current_date': now})
对子目录树的深度没有限制,你想要多少层都可以。 只要你喜欢,用多少层的子目录都无所谓。
注意
Windows用户必须使用斜杠而不是反斜杠。 get_template() 假定的是 Unix 风格的文件名符号约定。
windows下,斜杠,反斜杠都可以哦 写成这样都可以: return render_to_response('dateapp\current_datetime.html', {'dateapp/current_date': now,'happy':565135,'happy':'hahaha'})
include 模板标签
在讲解了模板加载机制之后,我们再介绍一个利用该机制的内建模板标签: {% include %} 。该标签允许在(模板中)包含其它的模板的内容。 标签的参数是所要包含的模板名称,可以是一个变量,也可以是用单/双引号硬编码的字符串。 每当在多个模板中出现相同的代码时,就应该考虑是否要使用 {% include %} 来减少重复。
下面这两个例子都包含了 nav.html 模板。这两个例子是等价的,它们证明单/双引号都是允许的。
{% include 'nav.html' %}
{% include "nav.html" %}
下面的例子包含了 includes/nav.html 模板的内容:
下面的例子包含了以变量 template_name 的值为名称的模板内容:
{% include template_name %}
也就是调用在模板目录里名为 template_name 的模板;换句话说,模板目录里的模板文件的文件名,就是一些变量, 调用这些变量,也就是调用这些模板
和在 get_template() 中一样, 对模板的文件名进行判断时会在所调取的模板名称之前加上来自 TEMPLATE_DIRS 的模板目录。
所包含的模板执行时的 context 和包含它们的模板是一样的。 举例说,考虑下面两个模板文件:
# mypage.html
<html>
<body>
{% include "includes/nav.html" %}
<h1>{{ title }}</h1>
</body>
</html>
# includes/nav.html
<div id="nav">
You are in: {{ current_section }}
</div>
如果你用一个包含 current_section的上下文去渲染 mypage.html这个模板文件,这个变量将存在于它所包含(include)的模板里,就像你想象的那样。
这个貌似和php的include不一样,include进来的只是模板,每次include后都需要重新传入context,麻烦了很多 不过一个好处是,模板的重用性变高了
“这个变量将存在于它所包含的模板里”,这句话的原文:then the variable will be available in the “included” template。觉得可以翻译成:那么就可以在它所包含的模板里使用这个变量(current_section)。
- 如果
DEBUG设置为True,你将会在 Django 错误信息页面看到TemplateDoesNotExist异常。 - 如果
DEBUG设置为False,该标签不会引发错误信息,在标签位置不显示任何东西。
模板继承
到目前为止,我们的模板范例都只是些零星的 HTML 片段,但在实际应用中,你将用 Django 模板系统来创建整个 HTML 页面。 这就带来一个常见的 Web 开发问题: 在整个网站中,如何减少共用页面区域(比如站点导航)所引起的重复和冗余代码?
解决该问题的传统做法是使用 服务器端的 includes ,你可以在 HTML 页面中使用该指令将一个网页嵌入到另一个中。 事实上, Django 通过刚才讲述的 {% include %} 支持了这种方法。 但是用 Django 解决此类问题的首选方法是使用更加优雅的策略—— 模板继承 。
本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。
让我们通过修改 current_datetime.html 文件,为 current_datetime 创建一个更加完整的模板来体会一下这种做法:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>The current time</title>
</head>
<body>
<h1>My helpful timestamp site</h1>
<p>It is now {{ current_date }}.</p>
<hr>
<p>Thanks for visiting my site.</p>
</body>
</html>
之前是这样返回给模板的 return render_to_response('current_datetime.html',{'current_date':now})
这看起来很棒,但如果我们要为第三章的 hours_ahead 视图创建另一个模板会发生什么事情呢?
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>Future time</title>
</head>
<body>
<h1>My helpful timestamp site</h1>
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
<hr>
<p>Thanks for visiting my site.</p>
</body>
</html>
很明显,我们刚才重复了大量的 HTML 代码。 想象一下,如果有一个更典型的网站,它有导航条、样式表,可能还有一些 JavaScript 代码,事情必将以向每个模板填充各种冗余的 HTML 而告终。
解决这个问题的服务器端 include 方案是找出两个模板中的共同部分,将其保存为不同的模板片段,然后在每个模板中进行 include。 也许你会把模板头部的一些代码保存为 header.html 文件:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
你可能会把底部保存到文件 footer.html :
<hr>
<p>Thanks for visiting my site.</p>
</body>
</html>
对基于 include 的策略,头部和底部的包含很简单。 麻烦的是中间部分。 在此范例中,每个页面都有一个 <h1>My helpful timestamp site</h1> 标题,但是这个标题不能放在 header.html 中,因为每个页面的 <title> 是不同的。 如果我们将 <h1> 包含在头部,我们就不得不包含 <title> ,但这样又不允许在每个页面对它进行定制。 何去何从呢?
Django 的模板继承系统解决了这些问题。 你可以将其视为服务器端 include 的逆向思维版本。 你可以对那些 不同 的代码段进行定义,而不是 共同 代码段。
第一步是定义 基础模板 , 该框架之后将由 子模板 所继承。 以下是我们目前所讲述范例的基础模板:
<!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>
这个叫做 base.html 的模板定义了一个简单的 HTML 框架文档,我们将在本站点的所有页面中使用。 子模板的作用就是重载、添加或保留那些块的内容。 (如果你一直按顺序学习到这里,保存这个文件到你的template目录下,命名为 base.html .)
我们使用一个以前已经见过的模板标签: {% block %} 。 所有的 {% block %} 标签告诉模板引擎,子模板可以重载这些部分。 每个{% block %}标签所要做的是告诉模板引擎,该模板下的这一块内容将有可能被子模板覆盖。
现在我们已经有了一个基本模板,我们可以修改 current_datetime.html 模板来 使用它:
{% extends "base.html" %} {% block title %}The current time{% endblock %} 把{% block title %}The current time{% endblock %}中The current time改为中文后报错'utf8' codec can't decode byte 0xc0 in position 48: invalid start byte,其他地方改中文没事
{% extends "base.html" %}
{% block title %}The current time{% endblock %}
{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}
这个地方把{% block title %}The current time{% endblock %}放在{% block content %}...{% endblock %}后面是没有影响其继承,结果是一样的
Invalid template name in 'extends' tag: ''. Got this from the 'base.html' variable
再为 hours_ahead 视图创建一个模板,看起来是这样的:
{% extends "base.html" %}
{% block title %}Future time{% endblock %}
{% block content %}
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
{% endblock %}
return render_to_response('hours_ahead.html', ({'hour_offset':offset,'next_time':dt}))
# 修改变量名,让locals()对应起来 def hours_ahead(request, hour_offset): try: hour_offset = int(hour_offset) except ValueError: raise Http404() next_time = datetime.datetime.now() + datetime.timedelta(hours=hour_offset) return render_to_response('dateapp/hours_ahead.html', locals())
# 修改变量名,让locals()对应起来 def hours_ahead(request, hour_offset): try: hour_offset = int(hour_offset) except ValueError: raise Http404() next_time = datetime.datetime.now() + datetime.timedelta(hours=hour_offset) return render_to_response('dateapp/hours_ahead.html', locals())
view里面hours_ahead函数修改变量名为模板需要调用的next_time,然后在render_to_response函数中使用locals(); return render_to_response('hours_ahead.html', locals())
文章中没有给出具体实现步骤,本来我只能打开/time/这个部分,/plus/部分的显示模版不存在,我参考了楼上的部分代码,具体操作如下: 在templates文件夹里分别新建base.html和hours_ahead.html两个文件,并把相应代码写进去,之后需要修改一下views.py里的hours_ahead函数,代码如下: def hours_ahead(request, hour_offset): try: hour_offset = int(hour_offset) except ValueError: raise Http404() next_time = datetime.datetime.now() + datetime.timedelta(hours=hour_offset) return render_to_response('hours_ahead.html', locals())
在这里用locals()就能明显看出来优势,但是locals()也不能乱用 return render(request,'hours_ahead.html',{'hour_offset':hour_offset,'next_time':next_time})=== return render(request,'hours_ahead.html',locals())
看起来很漂亮是不是? 每个模板只包含对自己而言 独一无二 的代码。 无需多余的部分。 如果想进行站点级的设计修改,仅需修改 base.html ,所有其它模板会立即反映出所作修改。
以下是其工作方式。 在加载 current_datetime.html 模板时,模板引擎发现了 {% extends %} 标签, 注意到该模板是一个子模板。 模板引擎立即装载其父模板,即本例中的 base.html 。
此时,模板引擎注意到 base.html 中的三个 {% block %} 标签,并用子模板的内容替换这些 block 。因此,引擎将会使用我们在 { block title %} 中定义的标题,对 {% block content %} 也是如此。 所以,网页标题一块将由 {% block title %}替换,同样地,网页的内容一块将由 {% block content %}替换。
{% block content %} <table> <colgroup> <col class="col1"> <col class="col2"> {% block tbody %}{% endblock %} </table> {% endblock %} 可以嵌套吗? 这样写可以吗?
其实这个模板继承很好用的,我开发所采用的flask框架的模板继承和这个是一样的。block里面可以嵌套block,但如果没有别的模板来继承你这个模板,你在该模板的block里面嵌套一个block就没任何意思了
{% extends "base.html" %} {% block title %}Future Time{% endblock %} {% extends "foot.html" %} <ExtendsNode: extends "foot.html"> must be the first tag in the template. 我改成下面这样 {% extends "base.html" %} {% extends "foot.html" %} {% block title %}Future Time{% endblock %} 'extends' cannot appear more than once in the same template 不可以继承多个的,不过也没必要啊
注意由于子模板并没有定义 footer 块,模板系统将使用在父模板中定义的值。 父模板 {% block %} 标签中的内容总是被当作一条退路。
继承并不会影响到模板的上下文。 换句话说,任何处在继承树上的模板都可以访问到你传到模板中的每一个模板变量。
你可以根据需要使用任意多的继承次数。 使用继承的一种常见方式是下面的三层法:
- 创建
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 %}的参数应该是字符串,但是如果直到运行时方能确定父模板名,这个参数也可以是个变量。 这使得你能够实现一些很酷的动态功能。
明白{{ block super }}的用法了。引入对应block后,在其前后,都可以增加相应的内容。如:{% block footer %} <b>This is the begin of footer.</b> {{ block.super }} <br/> <b>This is the end of footer.</b> {% endblock %}
这个是什么意思呀。。<ExtendsNode: extends "base.html"> must be the first tag in the template. 应该怎么改
@Jahangir 试过,在views的hours_ahead函数里添加var=“base.html”,直接通过local()传递到hours_ahead.html模板中,成功。
回复楼上的,如果你没有使用local()来传递变量,而是使用字典方式或者其他,那直接在子模板里这样写{% extends parent_template %},parent_template是变量名称,不要{% extends {{ parent_template }} %},是错误的。
{{ block.super }}:如果在基础模板中一个代码块定义了内容,然后在子模板中使用此代码块时,若使用‘block.super’方法,将引用基础模板中此代码块定义的内容,倘若不使用,那基础模板中此代码的定义的内容将不会显示。例子:基础模板‘{% block footer %}<hr><p>Thanks for visiting my site.</p>{% endblock %}’子模板:‘{% block footer %}{{ block.super }}<hr><p>This is the end of footer.</p> {% endblock %}’。既想显示基础模板footer块内容,又想显示子模板footer块添加的内容,就添加{{ block.super }} 标签吧。
模版继承,说白了就是父亲建的房子,儿子在父亲房子的基础上装修了一下,然后孙子长大了,想换一种风格,然后就又在父亲的基础上,把儿子的装修风格拆了,自己又装了一下,可以是部分的, 也可以是全部的
html写法:{% extends base %} # base需要与views函数的返回参数头一致 views函数写法:return render(request, 'current_datetime.html', {"base": "base.html"})
比如基模板如果有两个<% block title %>,继承这个模板的模板如果只重写一个<% blcok title %>,则无法判断填的是哪一个block的东西。
你现在已经掌握了模板系统的基本知识。 接下来呢?
时下大多数网站都是 数据库驱动 的:网站的内容都是存储在关系型数据库中。 这使得数据和逻辑能够彻底地分开(视图和模板也以同样方式对逻辑和显示进行了分隔。)
下一章将讲述如何与数据库打交道。