VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > python爬虫 >
  • python全栈开发中Django的深入模板引擎(2)

现在 docurrenttime() 把格式字符串和变量名传递给 CurrentTimeNode3 。

分析直至另一个块标签

模板标签可以像包含其它标签的块一样工作(想想 {% if %} 、 {% for %} 等)。 要创建一个这样的模板标签,在你的编译函数中使用 parser.parse() 。

标准的 {% comment %} 标签是这样实现的:

  def do_comment(parser, token):

      nodelist = parser.parse(('endcomment',))

      parser.delete_first_token()

      return CommentNode()



  class CommentNode(template.Node):

      def render(self, context):

          return ''

parser.parse() 接收一个包含了需要分析块标签名的元组作为参数. 它返回一个 django.template.NodeList 实例, 它是一个包含了所有 Node 对象的列表,这些对象代表了分析器在遇到元组中任一标签名之 前 的内容.

因此在前面的例子中, nodelist 是在 {% comment %} 和 {% endcomment %} 之间所有节点的列表,不包括 {% comment %} 和{% endcomment %} 自身。

在 parser.parse() 被调用之后,分析器还没有清除 {% endcomment %} 标签,因此代码需要显式地调用parser.deletefirsttoken() 来防止该标签被处理两次。

之后 CommentNode.render() 只是简单地返回一个空字符串。在 {% comment %} 和 {% endcomment %} 之间的所有内容都被忽略。

分析直至另外一个块标签并保存内容

1

在前一个例子中, do_comment() 抛弃了在 {% comment %} 和 {% endcomment %} 之间的所有内容。同样,也可以对块标签之间的代码进行处理。

例如,这个自定义模板标签: {% upper %} ,它把自己和 {% endupper %} 之间的所有内容都变成大写:

  {% upper %}

      This will appear in uppercase, {{ your_name }}.

  {% endupper %}

就像前面的例子一样,我们将使用 parser.parse() 。这次,我们将产生的 nodelist 传递给 Node :

  @register.tag

  def do_upper(parser, token):

      nodelist = parser.parse(('endupper',))

      parser.delete_first_token()

      return UpperNode(nodelist)



  class UpperNode(template.Node):



      def __init__(self, nodelist):

          self.nodelist = nodelist



      def render(self, context):

          output = self.nodelist.render(context)

          return output.upper()

这里唯一的一个新概念是 UpperNode.render() 中的 self.nodelist.render(context) 。它对节点列表中的每个 Node 简单的调用render() 。

更多的复杂渲染示例请查看 django/template/defaulttags.py 中的 {% if %} 、 {% for %} 、 {% ifequal %} 和{% ifchanged %} 的代码。

简单标签的快捷方式

许多模板标签接收单一的字符串参数或者一个模板变量 引用,然后独立地根据输入变量和一些其它外部信息进行处理并返回一个字符串. 例如, 我们先前写的 current_time 标签就是这样一个例子. 我们给它格式字符串, 然后它把时间作为字符串返回.

为了简化这类标签,Django 提供了一个帮助函数: simple_tag 。这个函数是 django.template.Library 的一个方法,它接受一个只有一个参数的函数作参数,把它包装在 render 函数和之前提及过的其他的必要单位中,然后通过模板系统注册标签。

我们之前的的 current_time 函数于是可以写成这样:

  def current_time(format_string):

      return datetime.datetime.now().strftime(format_string)

register.simpletag(currenttime)

在Python 2.4中,也可以使用修饰语法:

@register.simple_tag

def current_time(token):

...

有关 simple_tag 辅助函数,需要注意下面一些事情:

§ 传递给我们的函数的只有(单个)参数。

§ 在我们的函数被调用的时候,检查必需参数个数的工作已经完成了,所以我们不需要再做这个工作。

§ 参数两边的引号(如果有的话)已经被截掉了,所以我们会接收到一个普通字符串。

包含标签

另外一类常用的模板标签是通过渲染 其他 模板显示数据的。比如说,Django的后台管理界面,它使用了自定义的模板标签来显示新增/编辑表单页面下部的按钮。那些按钮看起来总是一样的,但是链接却随着所编辑的对象的不同而改变。这就是一个使用小模板很好的例子,这些小模板就是当前对象的详细信息。

这些排序标签被称为 包含标签 。如何写包含标签最好通过举例来说明。我们来写一个可以生成一个选项列表的多选项对象 Poll。标签这样使用:

  {% show_results poll %}

结果将会像下面这样:

  <ul>

    <li>First choice</li>

    <li>Second choice</li>

    <li>Third choice</li>

  </ul>

首先,我们定义一个函数,通过给定的参数生成一个字典形式的结果。 需要注意的是,我们只需要返回字典类型的结果就行了,它将被用做模板片断的context。 (译注:dict 的 key 作为变量名在模板中被使用)

  def show_books_for_author(author):

      books = author.book_set.all()

      return {'books': books}

接下来,我们创建用于渲染标签输出的模板。在我们的例子中,模板很简单:

  <ul>

  {% for book in books %}

      <li> {{ book }} </li>

  {% endfor %}

  </ul>

最后,我们通过对一个 Library 对象使用 inclusion_tag() 方法来创建并注册这个包含标签。

在我们的例子中,如果先前的模板在 polls/result_snippet.html 文件中,那么我们这样注册标签:

register.inclusiontag('books/booksforauthor.html')(showbooksforauthor)

和往常一样,我们也可以使用Python 2.4中的修饰语法,所以我们还可以这么写:

@register.inclusiontag('books/booksfor_author.html')

def showbooksforauthor(showbooksforauthor):

...

有时候,你的包含标签需要访问父模板的context。为了解决这个问题,Django提供了一个 takes_context 选项。如果你在创建模板标签时,指明了这个选项,这个标签就不需要参数,并且下面的Python函数会带一个参数:就是当这个标签被调用时的模板context。

例如,你正在写一个包含标签,该标签包含有指向主页的 homelink 和 hometitle 变量。Python函数会像这样:

  @register.inclusion_tag('link.html', takes_context=True)

  def jump_link(context):

      return {

          'link': context['home_link'],

          'title': context['home_title'],

      }

备注

函数的第一个参数 必须 是 context 。

模板 link.html 可能包含下面的东西:

Jump directly to {{ title }}.

然后您想使用自定义标签时,就可以加载它的库,然后不带参数地调用它,就像这样:

{% jump_link %}

编写自定义模板加载器

Djangos 内置的模板加载器(在先前的模板加载内幕章节有叙述)通常会满足你的所有的模板加载需求,但是如果你有特殊的加载需求的话,编写自己的模板加载器也会相当简单。比如:你可以从数据库加载模板,或者使用 Subversions的Python实现直接从Subversion库加载模板,再或者(稍后展示)从zip文件加载模板。

一个模板加载器,也就是 TEMPLATE_LOADERS 中的每一项,都要能被下面这个接口所调用:

loadtemplatesource(templatename, templatedirs=None)

参数 templatename 是所加载模板的名称 (和传递给 loader.gettemplate() 或者 loader.select_template() 一样), 而templatedirs 是一个可选的包含除去 TEMPLATEDIRS 之外的搜索目录列表。

如果加载器能够成功加载一个模板, 它应当返回一个元组: (templatesource, templatepath) 。在这里的 templatesource 就是将被模板引擎编译的的模板字符串,而 templatepath 是被加载的模板的路径。由于那个路径可能会出于调试目显示给用户,因此它应当很快的指明模板从哪里加载而来。

如果加载器加载模板失败,那么就会触发 django.template.TemplateDoesNotExist 异常。

每个加载函数都应该有一个名为 isusable 的函数属性。这个属性是一个布尔值,用于告知模板引擎这个加载器是否在当前安装的Python中可用。例如,如果 pkgresources 模块没有安装的话,eggs加载器(它能够从python eggs中加载模板)就应该把 isusable 设为 False ,因为必须通过 pkgresources 才能从eggs中读取数据。

一个例子可以清晰地阐明一切。这儿是一个模板加载函数,它可以从ZIP文件中加载模板。它使用了自定义的设置TEMPLATEZIPFILES 来取代了 TEMPLATE_DIRS 用作查找路径,并且它假设在此路径上的每一个文件都是包含模板的ZIP文件:

  import zipfile

  from django.conf import settings

  from django.template import TemplateDoesNotExist



  def load_template_source(template_name, template_dirs=None):

      """Template loader that loads templates from a ZIP file."""



      template_zipfiles = getattr(settings, "TEMPLATE_ZIP_FILES", [])



      # Try each ZIP file in TEMPLATE_ZIP_FILES.

      for fname in template_zipfiles:

          try:

              z = zipfile.ZipFile(fname)

              source = z.read(template_name)

          except (IOError, KeyError):

              continue

          z.close()

          # We found a template, so return the source.

          template_path = "%s:%s" % (fname, template_name)

          return (source, template_path)



      # If we reach here, the template couldn't be loaded

      raise TemplateDoesNotExist(template_name)



  # This loader is always usable (since zipfile is included with Python)

  load_template_source.is_usable = True

我们要想使用它,还差最后一步,就是把它加入到 TEMPLATELOADERS 。如果我们把这部分代码放到一个叫做 mysite.ziploader的包中,我们就需要把 mysite.ziploader.loadtemplatesource 加入到 TEMPLATELOADERS 中去。

使用内置的模板参考

Django管理界面包含一个完整的参考资料,里面有所有的可以在特定网站上使用的模板标签和过滤器。它设计的初衷是Django程序员提供给模板开发人员的一个工具。你可以点击管理页面右上角的文档链接来查看这些资料。

参考说明分为4个部分:标签、过滤器、模型和视图。 标签 和 过滤器 部分描述了所有内置的标签(实际上,第4章中用到的标签和过滤器都直接来源于那几页)以及一些可用的自定义标签和过滤器库。

视图 页面是最有价值的。网站中的每个URL都在这儿有独立的入口。如果相关的视图包含一个 文档字符串, 点击URL,你就会看到:

§ 生成本视图的视图函数的名字

§ 视图功能的一个简短描述

§ 上下文或一个视图模板中可用的变量的列表

§ 视图使用的模板的名字

要想查看关于视图文档的更详细的例子,请阅读Django的通用 objectlist 视图部分的源代码,它位于django/views/generic/listdetail.py 文件中。

通常情况下,由Django构建的网站都会使用数据库对象, 模型 页面描述了系统中所有类型的对象,以及该对象对应的所有可用字段。

总之,这些文档告诉你在模板中的所有可用的标签、过滤器、变量和对象。

配置独立模式下的模板系统

备注

这部分只针对于对在其他应用中使用模版系统作为输出组件感兴趣的人。如果你是在Django应用中使用模版系统,请略过此部分。

通常,Django会从它的默认配置文件和由 DJANGOSETTINGSMODULE 环境变量所指定的模块中加载它需要的所有配置信息。但是当你想在非Django应用中使用模版系统的时候,采用环境变量并不是很好的方法。比起为模版系统单独采用配置文件并用环境变量来指向它,你可能更希望能够在你的应用中采用一致的配置方法来配置模版系统和其他部分

为了解决这个问题,你需要使用附录E中所描述的手动配置选项。简单来说,你需要引入合适的模板系统,并且在调用任何模板函数 之前 调用 django.conf.settings.configure() 来指定任何你想要的设置。

你可能会考虑至少要设置 TEMPLATEDIRS (如果你打算使用模板加载器), DEFAULTCHARSET (尽管默认的 utf-8 编码相当好用),以及 TEMPLATEDEBUG 。所有可用的选项都在附录E中详细描述,所有以 TEMPLATE 开头的选项都可能使你感兴趣的。

接下来?

迄今为止,本书假定您想展示的内容为HTML。对于一个有关Web开发的书来说,这不是一个 不好的假设,但有时你想用Django输出其他数据格式。

 



相关教程
          
关于我们--广告服务--免责声明--本站帮助-友情链接--版权声明--联系我们       黑ICP备07002182号