Django项目实践2 - Django模板(view-html)

来源:互联网 发布:三国乱世盘古挂机软件 编辑:程序博客网 时间:2024/05/16 23:56

http://blog.csdn.net/pipisorry/article/details/45061511

上篇:Django项目实践1 - 创建Django项目

Django模板

{视图和模板对逻辑和显示进行了分隔}

上面是使用 django.http.HttpResponse() 来输出"Hello World!"。该方式将数据与视图混合在一起,不符合Django的MVC思想。或者HTML被直接硬编码在 Python 代码之中。

html = "<html><body>It is now %s.</body></html>" % now

return HttpResponse(html)

Django 模板:模板是一个文本,用于分离文档的表现形式和内容。 模板定义了占位符以及各种用于规范文档该如何显示的各部分基本逻辑(模板标签)。 模板通常用于产生HTML,但是Django的模板也能产生任何基于文本格式的文档。

这样做的好处:

对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多。

ython 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。 设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作。

程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。

将页面的设计和Python的代码分离开会更干净简洁更容易维护。 使用 Django的 模板系统 (Template System)来实现这种模式。

通常将模板和视图一起使用,但是模板系统是一个Python库,你可以在任何地方使用它,而不仅仅是在Django视图中。

模板系统的使用-Python解释器

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

可以用原始的模板代码字符串创建一个 Template 对象, Django同样支持用指定模板文件路径的方式来创建Template 对象;

调用模板对象的render方法,并且传入一套变量context。它将返回一个基于模板的展现字符串,模板中的变量和标签会被context值替换。

使用Python的解释器使用模板的示例

转到project目录(在第二章由 django-admin.pystartproject 命令创建), 输入命令pythonmanage.pyshell 启动交互界面。

使用Django模板系统的基本规则

写模板,创建 Template 对象,创建Context , 调用render() 方法。

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

1. 我们运行python manage.py shell而不是python。这两个命令都会启动交互解释器,但是manage.pyshell命令有一个重要的不同: 在启动解释器之前,它告诉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.pyshell``这个方法,这样可以免去配置那些环境变量。随着你越来越熟悉Django,你可能会偏向于废弃使用`` manage.py shell``,而是在你的配置文件.bash_profile中手动添加DJANGO_SETTINGS_MODULE这个环境变量。

2. 创建Template 对象最简单的方法就是直接实例化它。当你创建一个Template 对象,模板系统在内部编译这个模板到内部格式,并做优化,做好 渲染的准备。

3. 模板渲染:用 context 来传递数据给模板。 一个context是一系列变量和它们值的集合。context在Django里表现为 Context 类,在 django.template 模块里。 她的构造函数带有一个可选的参数: 一个字典映射变量和它们的值。 调用 Template 对象 的 render() 方法并传递context来填充模板。t.render(c)返回的值是一个Unicode对象,不是普通的Python字符串。 在框架中,Django会一直使用Unicode对象而不是普通的字符串。

4. 在模板对象上调用 render() 方法,传递 context参数给它。 这是返回渲染后的模板的方法,它会替换模板变量为真实的值和执行块标签。

5. 使用同一模板源渲染多个context,只进行 一次模板创建然后多次调用render()方法渲染会更为高效:

# Goodt = Template('Hello, {{ name }}')for name in ('John', 'Julie', 'Pat'):    print t.render(Context({'name': name}))
Django 模板解析非常快捷。 大部分的解析工作都是在后台通过对简短正则表达式一次性调用来完成。 这和基于 XML 的模板引擎形成鲜明对比,那些引擎承担了 XML 解析器的开销,且往往比 Django 模板渲染引擎要慢上几个数量级。

6. 输出里有回车换行的字符('\n' )而不是 显示回车换行? 因为这是Python交互解释器的缘故: 调用 t.render(c) 返回字符串, 解释器缺省显示这些字符串的 真实内容呈现 ,而不是打印这个变量的值。 要显示换行而不是 '\n' ,使用 print 语句: print t.render(c)上下文(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'
深度变量的查找

Django 模板中遍历复杂数据结构的关键是句点字符 (.)。

通过实例变量加一点(dots)来访问它的属性,这个方法适用于任意的对象:

>>> person = {'name': 'Sally', 'age': '43'}>>> t = Template('{{ person.name }} is {{ person.age }} years old.')
>>> d = datetime.date(1993, 5, 2)>>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.')
点语法也可以用来引用对象的* 方法*:
>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')>>> t.render(Context({'var': 'hello'}))u'hello -- HELLO -- False'
Note:
1. 这里调用方法时并* 没有* 使用圆括号 而且也无法给该方法传递参数;你只能调用不需参数的方法。
2. 仅在方法无需传入参数时,其调用才有效。 否则,系统将会转移到下一个查找类型(列表索引查找)。
句点也可用于访问列表索引:
>>> t = Template('Item 2 is {{ items.2 }}.')>>> c = Context({'items': ['apples', 'bananas', 'carrots']})
Note:不允许使用负数列表索引。

句点查找规则概括:

当模板系统在变量名中遇到点时,按照以下顺序尝试进行查找:

  • 字典类型查找 (比如 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.')

方法调用行为

方法调用比其他类型的查找略为复杂一点。 注意事项:

在方法查找过程中,如果某方法抛出一个异常,除非该异常有一个 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 .'

显然,有些方法是有副作用的,好的情况下允许模板系统访问它们可能只是干件蠢事,坏的情况下甚至会引发安全漏洞。

例如,你的一个 BankAccount 对象有一个delete() 方法。如果某个模板中包含了像{{account.delete}}这样的标签,其中`` account`` 又是BankAccount 的一个实例,请注意在这个模板载入时,account对象将被删除。要防止这样的事情发生,必须设置该方法的alters_data 函数属性:

def delete(self):    # Delete the accountdelete.alters_data = True
模板系统不会执行任何以该方式进行标记的方法。 接上面的例子,如果模板文件里包含了 {{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 .'
对于一个web站点来说,如果仅仅因为一个小的模板语法错误而造成无法访问,这是不可接受的。



模板系统的使用-视图中的应用

重新打开前面VoteSite.views 中创建的current_datetime 视图:

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

让我们用 Django 模板系统来修改该视图,可能的修改:

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)
Note:上面确实使用了模板系统,但是并没有解决我们在本章开头所指出的问题。 也就是说,模板仍然嵌入在Python代码里,并未真正的实现数据与表现的分离。

下面采用模板自加载 跟 模板目录 的技巧:

模板加载

为了减少模板加载调用过程及模板本身的冗余代码,Django 提供了一种使用方便且功能强大的 API ,用于从磁盘中加载模板,要使用此模板加载API,首先你必须将模板的保存位置告诉框架。 设置的保存文件就是我们前一章节讲述ROOT_URLCONF配置的时候提到的settings.py。找到TEMPLATE_DIRS这项设置吧。 它的默认设置是一个空元组(tuple),加上一些自动生成的注释。

选择一个目录用于存放模板并将其添加到 TEMPLATE_DIRS 中:

TEMPLATE_DIRS = (    '/home/django/mysite/templates',)

Note:

1. 建议在 Django 项目中创建一个 templates 目录。如果你的TEMPLATE_DIRS只包含一个目录,别忘了在该目录后加上个逗号。Python 要求单元素元组中必须使用逗号,以此消除与圆括号表达式之间的歧义。

2. 如果使用的是 Windows 平台,请包含驱动器符号并使用Unix风格的斜杠(/)而不是反斜杠()

3. 最省事的方式是使用绝对路径(即从文件系统根目录开始的目录路径)。 如果想要更灵活一点并减少一些负面干扰,可利用 Django 配置文件就是 Python 代码这一点来动态构建TEMPLATE_DIRS 的内容:

import os.pathTEMPLATE_DIRS = (    os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),)
Note:这个例子使用了神奇的 Python 内部变量 __file__ ,该变量被自动设置为代码所在的 Python 模块文件名。 `` os.path.dirname(__file__)`` 将会获取自身所在的文件,即settings.py 所在的目录,然后由os.path.join 这个方法将这目录与 templates 进行连接。如果在windows下,它会智能地选择正确的后向斜杠”“进行连接,而不是前向斜杠”/”。

我们接着上面项目将在 HelloWorld 目录底下创建 templates 目录并建立 hello.html文件,整个目录结构如下:

HelloWorld/|-- HelloWorld|   |-- __init__.py|   |-- __init__.pyc|   |-- settings.py|   |-- settings.pyc|   |-- urls.py|   |-- urls.pyc|   |-- view.py|   |-- view.pyc|   |-- wsgi.py|   `-- wsgi.pyc|-- manage.py`-- templates    `-- hello.html

hello.html 文件代码如下:

<h1>{{ hello }}</h1>
Note:关于html标签参阅[HTML教程 - 基本元素/标签及属性]
Django 视图(view.py)和Django 模板(templates中的html)

templates中的html文件决定了数据怎么显示(如上面的hello是以大标题的形式显示的),只是一个模板(没有真实数据),用于渲染数据。

下面修改后的view.py决定视图/html中要显示的数据/变量(模板中变量使用了双括号)是什么(如上面html中的hello代表'Hello World!')。

Note:需要注意的是,不能简单的把 Django 视图认为是MVC控制器,把 Django 模板认为MVC视图。 区别在于:
     Django 视图 不处理用户输入,而仅仅决定要展现哪些数据给用户;
     Django 模板 仅仅决定如何展现Django视图指定的数据。

或者说, Django将MVC中的视图进一步分解为 Django视图 和 Django模板两个部分,分别决定 “展现哪些数据” 和 “如何展现”,使得Django的模板可以根据需要随时替换,而不仅仅限制于内置的模板。

改 views.py,增加一个新的对象,用于向html模板提交数据
# -*- coding: utf-8 -*-from django.shortcuts import renderdef hello(request):    context          = {}    context['hello'] = 'Hello pipi!'    return render(request, 'hello.html', context)

Note:

1. 这里使用render来替代之前使用的HttpResponse。render还使用了一个字典context作为参数。 render_to_response() 只是对 get_template() 的简单封装。可以把模板存放在你模板目录的子目录中。注意:Windows用户必须使用斜杠而不是反斜杠。get_template() 假定的是 Unix 风格的文件名符号约定。

2. context 字典中元素的键值 "hello" 对应了模板中的变量 "{{ hello }}"。

3. 以上代码比较low的写法:

def current_datetime(request):    now = datetime.datetime.now()    t = get_template('current_datetime.html')    html = t.render(Context({'current_date': now}))

4.另一种更高大上的写法

def current_datetime(request):    current_date = datetime.datetime.now()    return render_to_response('current_datetime.html', locals())
在此,我们没有像之前那样手工指定 context 字典,而是传入了 locals() 的值,它囊括了函数执行到该时间点时所定义的一切变量。 因此,我们将now 变量重命名为current_date ,因为那才是模板所预期的变量名称。 在本例中,locals() 并没有带来多 的改进,但是如果有多个模板变量要界定而你又想偷懒,这种技术可以减少一些键盘输入。使用locals() 时要注意是它将包括 所有 的局部变量,它们可能比你想让模板访问的要多。 在前例中, locals() 还包含了request 。对此如何取舍取决你的应用程序。

浏览器中查看结果

在浏览器访问打开浏览器并访问:192.168.*.*:8000/hello/

输出:Hello pipi!

这样我们就完成了使用模板来输出数据,从而实现数据(view中的数据)与视图(html文件)分离。



添加html表现

在创建项目的 templates 目录中添加 base.html 文件

<html>  <head>    <title>Title pipi!</title>  </head>  <body>    <h1>Hello pipi!</h1>    {% block mainbody %}       <p>original</p>    {% endblock %}  </body></html>

hello.html中继承base.html,并替换特定block,hello.html修改后的代码如下:

{% extends "base.html" %}{% block mainbody %}<p>继承base.html文件, 替换original</p>{% endblock %}

Note:第一行代码说明hello.html继承了 base.html 文件。可以看到,这里相同名字的block标签用以替换base.html的相应block。

语法参见 - Django模板(常用语法规则)



浏览器中访问

重新访问地址http://192.168.*.*:8000/hello/

输出:

下篇:Django项目实践3 - Django模型

from:http://blog.csdn.net/pipisorry/article/details/45061511


1 0