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

第十章: 深入模板引擎

虽然和Django的模板语言的大多数交互都是模板作者的工作,但你可能想定制和扩展模板引擎,让它做一些它不能做的事情,或者是以其他方式让你的工作更轻松。

python入门教程本章深入钻研Django的模板系统。如果你想扩展模板系统或者只是对它的工作原理感觉到好奇,本章涉及了你需要了解的东西。

如果你想把Django的模版系统作为另外一个应用程序的一部分(比如,仅使用django的模板系统而不使用Django框架的其他部分),那你一定要读一下“配置独立模式下的模版系统”这一节。

模板语言回顾

python入门教程首先,让我们快速回顾一下第四章介绍的若干专业术语

模板 是一个纯文本文件,或是一个用Django模板语言标记过的普通的Python字符串,一个模板可以包含区块标签和变量。

区块标签 是在一个模板里面起作用的的标记,这个定义故意说的很含糊,比如,一个 区块标签可以生成内容,可以作为一个控制结构( if 语句或 for 循环), 可以获取数据库内容,或者访问其他的模板标签。

区块标签被 {% 和 %} 环绕:

  {% if is_logged_in %}

    Thanks for logging in!

  {% else %}

    Please log in.

  {% endif %}

变量 是一个在模板里用来输出值的标记。

变量标签被 {{ 和 }} 环绕:

  My first name is {{ first_name }}. My last name is {{ last_name }}.

context 是一个传递给模板的名称到值的映射(类似Python字典)。

模板 渲染 就是是通过从context获取值来替换模板中变量并执行所有的区块标签。

关于这些基本概念更详细的内容,请参考第四章。

本章的其余部分讨论了扩展模板引擎的方法。首先,我们快速的看一下第四章遗留的内容。

RequestContext和Context处理器

你需要一段context来解析模板。一般情况下,这是一个 django.template.Context 的实例,不过在Django中还可以用一个特殊的子类, django.template.RequestContext ,这个运用起来稍微有些不同。 RequestContext 默认地在模板context中加入了一些变量,如 HttpRequest 对象或当前登录用户的相关信息。

当你不想在一系例模板中都明确指定一些相同的变量时,你应该使用 RequestContext 。例如,看下面的四个视图:

  from django.template import loader, Context



  def view_1(request):

      # ...

      t = loader.get_template('template1.html')

      c = Context({

          'app': 'My app',

          'user': request.user,

          'ip_address': request.META['REMOTE_ADDR'],

          'message': 'I am view 1.'

      })

      return t.render(c)



  def view_2(request):

      # ...

      t = loader.get_template('template2.html')

      c = Context({

          'app': 'My app',

          'user': request.user,

          'ip_address': request.META['REMOTE_ADDR'],

          'message': 'I am the second view.'

      })

      return t.render(c)



  def view_3(request):

  # ...

      t = loader.get_template('template3.html')

      c = Context({

          'app': 'My app',

          'user': request.user,

          'ip_address': request.META['REMOTE_ADDR'],

          'message': 'I am the third view.'

      })

      return t.render(c)



  def view_4(request):

      # ...

      t = loader.get_template('template4.html')

      c = Context({

          'app': 'My app',

          'user': request.user,

          'ip_address': request.META['REMOTE_ADDR'],

          'message': 'I am the fourth view.'

      })

      return t.render(c)

(注意,在这些例子中,我们故意 不 使用 rendertoresponse() 这个快捷方法,而选择手动载入模板,手动构造context对象然后渲染模板。是为了能够清晰的说明所有步骤。)

每个视图都给模板传入了三个相同的变量: app 、 user 和 ip_address 。如果我们能把这些冗余去掉会不会看起来更好?

创建 RequestContext 和 context处理器 就是为了解决这个问题。Context处理器允许你设置一些变量,它们会在每个context中自动被设置好,而不必每次调用 rendertoresponse() 时都指定。要点就是,当你渲染模板时,你要用 RequestContext 而不是 Context 。

最直接的做法是用context处理器来创建一些处理器并传递给 RequestContext 。上面的例子可以用context processors改写如下:

  from django.template import loader, RequestContext



  def custom_proc(request):

      "A context processor that provides 'app', 'user' and 'ip_address'."

      return {

          'app': 'My app',

          'user': request.user,

          'ip_address': request.META['REMOTE_ADDR']

      }



  def view_1(request):

      # ...

      t = loader.get_template('template1.html')

      c = RequestContext(request, {'message': 'I am view 1.'},

              processors=[custom_proc])

      return t.render(c)



  def view_2(request):

      # ...

      t = loader.get_template('template2.html')

      c = RequestContext(request, {'message': 'I am the second view.'},

              processors=[custom_proc])

      return t.render(c)



  def view_3(request):

      # ...

      t = loader.get_template('template3.html')

      c = RequestContext(request, {'message': 'I am the third view.'},

              processors=[custom_proc])

      return t.render(c)



  def view_4(request):

      # ...

      t = loader.get_template('template4.html')

      c = RequestContext(request, {'message': 'I am the fourth view.'},

              processors=[custom_proc])

      return t.render(c)

我们来通读一下代码:

§ 首先,我们定义一个函数 custom_proc 。这是一个context处理器,它接收一个 HttpRequest 对象,然后返回一个字典,这个字典中包含了可以在模板context中使用的变量。它就做了这么多。

§ 我们在这四个视图函数中用 RequestContext 代替了 Context 。在context对象的构建上有两个不同点。一,RequestContext 的第一个参数需要传递一个 HttpRequest 对象,就是传递给视图函数的第一个参数( request )。二,RequestContext 有一个可选的参数 processors ,这是一个包含context处理器函数的list或者tuple。在这里,我们传递了我们之前定义的函数 curstom_proc 。

§ 每个视图的context结构里不再包含 app 、 user 、 ipaddress 等变量,因为这些由 customproc 函数提供了。

§ 每个视图 仍然 具有很大的灵活性,可以引入我们需要的任何模板变量。在这个例子中, message 模板变量在每个视图中都不一样。

在第四章,我们介绍了 rendertoresponse() 这个快捷方式,它可以省掉调用 loader.gettemplate() ,然后创建一个 Context对象,最后再调用模板对象的 render() 方法。为了讲解context处理器底层是如何工作的,在上面的例子中我们没有使用rendertoresponse() 。但是建议选择 rendertoresponse() 作为context的处理器。像这样,使用 contextinstance 参数:

  from django.shortcuts import render_to_response

  from django.template import RequestContext



  def custom_proc(request):

      "A context processor that provides 'app', 'user' and 'ip_address'."

      return {

          'app': 'My app',

          'user': request.user,

          'ip_address': request.META['REMOTE_ADDR']

      }



  def view_1(request):

      # ...

      return render_to_response('template1.html',

          {'message': 'I am view 1.'},

          context_instance=RequestContext(request, processors=[custom_proc]))



  def view_2(request):

      # ...

      return render_to_response('template2.html',

          {'message': 'I am the second view.'},

          context_instance=RequestContext(request, processors=[custom_proc]))



  def view_3(request):

      # ...

      return render_to_response('template3.html',

          {'message': 'I am the third view.'},

          context_instance=RequestContext(request, processors=[custom_proc]))



  def view_4(request):

      # ...

      return render_to_response('template4.html',

          {'message': 'I am the fourth view.'},

          context_instance=RequestContext(request, processors=[custom_proc]))

在这,我们将每个视图的模板渲染代码写成了一个单行。

虽然这是一种改进,但是,请考虑一下这段代码的简洁性,我们现在不得不承认的是在 另外 一方面有些过分了。我们以代码冗余(在 processors 调用中)的代价消除了数据上的冗余(我们的模板变量)。由于你不得不一直键入 processors ,所以使用context处理器并没有减少太多的打字次数。

Django因此提供对 全局 context处理器的支持。 TEMPLATECONTEXTPROCESSORS 指定了 总是 使用哪些 context processors 。这样就省去了每次使用 python入门教程RequestContext 都指定 processors 的麻烦^_^。

默认情况下, TEMPLATECONTEXTPROCESSORS 设置如下:

  TEMPLATE_CONTEXT_PROCESSORS = (

      'django.core.context_processors.auth',

      'django.core.context_processors.debug',

      'django.core.context_processors.i18n',

      'django.core.context_processors.media',

  )

这个设置是一个可调用函数的Tuple,其中的每个函数使用了和上文中我们的 customproc 相同的接口:接收一个request对象作为参数,返回一个包含了将被合并到context中的项的字典。请注意 TEMPLATECONTEXT_PROCESSORS 中的值是以 strings 的形式给出的,这意味着这些处理器必须在你的python路径中的某处(这样你才能在设置中引用它们)

每个处理器将会按照顺序应用。也就是说如果你在第一个处理器里面向context添加了一个变量,而第二个处理器添加了同样名字的变量,那么第二个将会覆盖第一个。

Django提供了几个简单的context处理器,有些在默认情况下被启用的。

django.core.context_processors.auth

如果 TEMPLATECONTEXTPROCESSORS 包含了这个处理器,那么每个 RequestContext 将包含这些变量:

§ user :一个 django.contrib.auth.models.User 实例,描述了当前登录用户(或者一个 AnonymousUser 实例,如果客户端没有登录)。

§ messages :一个当前登录用户的消息列表(字符串)。在后台,对每一个请求这个变量都调用request.user.getanddelete_messages() 方法。这个方法收集用户的消息然后把它们从数据库中删除。

§ perms : django.core.context_processors.PermWrapper 的一个实例,包含了当前登录用户有哪些权限。

关于users、permissions和messages的更多内容请参考第12章。

django.core.context_processors.debug

这个处理器把调试信息发送到模板层。如果 TEMPLATECONTEXTPROCESSORS 包含了这个处理器, RequestContext 将包含这些变量:

§ debug :你设置的 DEBUG 的值( True 或 False )。你可以在模板里面用这个变量测试是否处在debug模式下。

§ sql_queries :包含类似于 {'sql': ..., 'time': ...} 的字典的一个列表, 记录了这个请求期间的每个SQL查询以及查询所耗费的时间。这个列表是按照请求顺序进行排列的。

由于调试信息比较敏感,所以这个context处理器只有当同时满足下面两个条件的时候才有效:

§ DEBUG 参数设置为 True 。

§ 请求的ip应该包含在 INTERNAL_IPS 的设置里面。

django.core.context_processors.i18n

如果这个处理器启用,每个 RequestContext 将包含下面的变量:

§ LANGUAGES : LANGUAGES 选项的值。

§ LANGUAGECODE :如果 request.LANGUAGECODE 存在,就等于它;否则,等同于 LANGUAGE_CODE 设置。

附录E提供了有关这两个设置的更多的信息。

django.core.context_processors.request

如果启用这个处理器,每个 RequestContext 将包含变量 request , 也就是当前的 HttpRequest 对象。注意这个处理器默认是不启用的,你需要激活它。

写Context处理器的一些建议

编写处理器的一些建议:

§ 使每个context处理器完成尽可能小的功能。 使用多个处理器是很容易的,所以你可以根据逻辑块来分解功能以便将来重用。

§ 要注意 TEMPLATECONTEXTPROCESSORS 里的context processor 将会在 每个 模板中有效,所以要变量的命名不要和模板的变量冲突。 变量名是大小写敏感的,所以processor的变量全用大写是个不错的主意。

§ 只要它们存放在你的Python的搜索路径中,它们放在哪个物理路径并不重要,这样你可以在TEMPLATECONTEXTPROCESSORS 设置里指向它们。 也就是说,你要把它们放在app或者project目录里名为context_processors.py 的文件。

模板加载的内幕

一般说来,你会把模板以文件的方式存储在文件系统中,但是你也可以使用自定义的 template loaders 从其他来源加载模板。

Django有两种方法加载模板

§ django.template.loader.gettemplate(templatename) : get_template 根据给定的模板名称返回一个已编译的模板(一个 Template 对象)。如果模板不存在,就触发 TemplateDoesNotExist 的异常。

§ django.template.loader.selecttemplate(templatename_list) : selecttemplate 很像 gettemplate ,不过它是以模板名称的列表作为参数的,并且它返回第一个存在的模板。如果模板都不存在,将会触发 TemplateDoesNotExist 异常。

正如在第四章中所提到的,默认情况下这些函数使用 TEMPLATE_DIRS 的设置来载入模板。但是,在内部这些函数可以指定一个模板加载器来完成这些繁重的任务。

一些加载器默认被禁用,但是你可以通过编辑 TEMPLATELOADERS 设置来激活它们。 TEMPLATELOADERS 应当是一个字符串的元组,其中每个字符串都表示一个模板加载器。这些模板加载器随Django一起发布。

django.template.loaders.filesystem.loadtemplatesource : 这个加载器根据TEMPLATE_DIRS 的设置从文件系统加载模板。在默认情况下这个加载器被启用.

django.template.loaders.appdirectories.loadtemplatesource : 这个加 载器从文件系统上的Django应用中加载模板。对 INSTALLEDAPPS 中的每个应用,这个加 载器会查找一个templates 子目录。如果这个目录存在,Django就在那里寻找模板。

这意味着你可以把模板和你的应用一起保存,从而使得Django应用更容易和默认模板一起发布。例如,如果 INSTALLEDAPPS 包含 ('myproject.polls','myproject.music') ,那么gettemplate('foo.html') 会按这个顺序查找模板:

  §                     /path/to/myproject/polls/templates/foo.html

  §                     /path/to/myproject/music/templates/foo.html

请注意加载器在首次被导入的时候会执行一个优化:它会缓存一个列表,这个列表包含了INSTALLED_APPS 中带有 templates 子目录的包。

这个加载器默认启用。

django.template.loaders.eggs.loadtemplatesource : 这个加载器类似 app_directories,只不过它从Python eggs而不是文件系统中加载模板。这个加载器默认被禁用;如果你使用eggs来发布你的应用,那么你就需要启用它。

Django按照 TEMPLATE_LOADERS 设置中的顺序使用模板加载器。它逐个使用每个加载器直至找到一个匹配的模板。

扩展模板系统

既然你已经对模板系统的内幕了解多了一些,让我们来看看如何使用自定义的代码来拓展这个系统吧。

绝大部分的模板定制是以自定义标签和/或过滤器的方式来完成的。尽管Django模板语言自带了许多内建标签和过滤器,但是你可能还是需要组建你自己的标签和过滤器库来满足你的需要。幸运的是,定义你自己的功能非常容易。

创建一个模板库

不管是写自定义标签还是过滤器,第一件要做的事是给 template library 创建使Django能够勾入的机制。

创建一个模板库分两步走:

第一,决定哪个Django应用应当拥有这个模板库。如果你通过 manage.py startapp 创建了一个应用,你可以把它放在那里,或者你可以为模板库单独创建一个应用。

无论你采用何种方式,请确保把你的应用添加到 INSTALLED_APPS 中。我们稍后会解释这一点。

第二,在适当的Django应用包里创建一个 templatetags 目录。这个目录应当和 models.py 、views.py 等处于同一层次。例如:

  books/

      __init__.py

      models.py

      templatetags/

      views.py

在 templatetags 中创建两个空文件:一个 init.py (告诉Python这是 一个包含了Python代码的包)和一个用来存放你自定义的标签/过滤器定义的文件。第二个文件 的名字稍后将用来加载标签。例如,如果你的自定义标签/过滤器在一个叫作 poll_extras.py 的文件中,你需要在模板中写入如下内容:

  {% load poll_extras %}

{% load %} 标签检查 INSTALLED_APPS 中的设置,仅允许加载已安装的Django应用程序中的模板库。这是一个安全特性。它可以让你在一台电脑上部署很多的模板库的代码,而又不用把它们暴露给每一个Django安装。

如果你写了一个不和任何模型/视图关联的模板库,那么得到一个仅包含 templatetags 包的Django应用程序包是完全正常的。对于在 templatetags 包中放置多少个模块没有做任何的限制。需要了解的是: {% load %} 语句会为指定的Python模块名(而非应用程序名)加载标签或过滤器。

一旦创建了Python模块,你只需根据是要编写过滤器还是标签来相应的编写一些Python代码。

要成为有效的标签库,模块必须包含一个模块级的变量: register ,这是一个 template.Library 的实例。这个template.Library 实例是包含所有已注册的标签及过滤器的数据结构。因此,在模块的顶部位置插入下述代码:

  from django import template



  register = template.Library()

备注

请阅读Django默认的过滤器和标签的源码,那里有大量的例子。他们分别为: django/template/defaultfilters.py 和django/template/defaulttags.py 。某些应用程序在 django.contrib 中也包含模板库。

创建 register 变量后,你就可以使用它来创建模板的过滤器和标签了。

自定义模板过滤器

自定义过滤器就是有一个或两个参数的Python函数:

§ (输入)变量的值

§ 参数的值, 可以是默认值或者完全留空

例如,在过滤器 {{ var|foo:"bar" }} 中 ,过滤器 foo 会被传入变量 var 和参数 bar 的内容。

过滤器函数应该总有返回值,而且不能触发异常,它们都应该静静的失败。如果有一个错误发生,它们要么返回原始的输入字符串,要么返回空的字符串,无论哪个都可以。

这里是一些定义过滤器的例子:

  def cut(value, arg):

      "Removes all values of arg from the given string"

      return value.replace(arg, '')

这里是一些如何使用过滤器的例子:

  {{ somevariable|cut:"0" }}

大多数过滤器并不需要参数。下面的例子把参数从你的函数中拿掉了:

  def lower(value): # Only one argument.

      "Converts a string into all lowercase"

      return value.lower()

当你在定义你的过滤器时,你需要用 Library 实例来注册它,这样就能通过Django的模板语言来使用了:

  register.filter('cut', cut)

  register.filter('lower', lower)

  Library.filter() 方法需要两个参数:

  §                     过滤器的名称(一个字串)

  §                     过滤器函数本身

如果你使用的是Python 2.4或更新,你可以使用 register.filter() 作为一个装饰器:

  @register.filter(name='cut')

  def cut(value, arg):

      return value.replace(arg, '')



  @register.filter

  def lower(value):

      return value.lower()

像第二个例子中,如果你不使用 name 参数,那么Django将会使用函数名作为过滤器的名字。

下面是一个完整的模板库的例子,提供了一个 cut 过滤器:

  from django import template



  register = template.Library()



  @register.filter(name='cut')

  def cut(value, arg):

      return value.replace(arg, '')

自定义模板标签

标签要比过滤器复杂些,标签几乎能做任何事情。

第四章描述了模板系统的两步处理过程:编译和呈现。为了自定义一个这样的模板标签,你需要告诉Django当遇到你的标签时怎样进行这过程。

当Django编译一个模板时,它将原始模板分成一个个 节点 。每个节点都是 django.template.Node 的一个实例,并且具备render() 方法。于是,一个已编译的模板就是 Node 对象的一个列表。

当你调用一个已编译模板的 render() 方法时,模板就会用给定的context来调用每个在它的节点列表上的节点的 render() 方法。所以,为了定义一个自定义的模板标签,你需要明确这个模板标签转换为一个 Node (已编译的函数)和这个node的render() 方法。

在下面的章节中,我们将详细解说写一个自定义标签时的所有步骤。

编写编译函数

当遇到一个模板标签(template tag)时,模板解析器就会把标签包含的内容,以及模板解析器自己作为参数调用一个python函数。这个函数负责返回一个和当前模板标签内容相对应的节点(Node)的实例。

例如,写一个显示当前日期的模板标签:{% current_time %},该标签会根据参数指定的 strftime 格式(参见:http://www.djangoproject.com/r/python/strftime/ )显示当前时间。在继续做其它事情以前,先决定标签的语法是一个好主意。在我们的例子里,该标签将会像这样被使用:

The time is {% current_time "%Y-%m-%d %I:%M %p" %}.

备注

没错, 这个模板标签是多余的,Django默认的 {% now %} 用更简单的语法完成了同样的工作。这个模板标签在这里只是作为一个例子。

这个函数的分析器会获取参数并创建一个 Node 对象:

  from django import template



  def do_current_time(parser, token):

      try:

          # split_contents() knows not to split quoted strings.

          tag_name, format_string = token.split_contents()

      except ValueError:

          msg = '%r tag requires a single argument' % token.contents[0]

          raise template.TemplateSyntaxError(msg)

      return CurrentTimeNode(format_string[1:-1])

1

其实这儿包含了不少东西:

§ parser 是模板分析器对象,在这个例子中我们没有使用它。

§ token.contents 是包含有标签原始内容的字符串。在我们的例子中,它是 'current_time "%Y-%m-%d %I:%M %p"' 。

§ token.split_contents() 方法按空格拆分参数同时保证引号中的字符串在一起。应该避免使用 token.contents.split()(仅是使用Python的标准字符串拆分),它不够健壮,因为它只是简单的按照 所有 空格进行拆分,包括那些引号引起来的字符串中的空格。

§ 这个函数负责抛出 django.template.TemplateSyntaxError ,同时提供所有语法错误的有用信息。

§ 不要把标签名称硬编码在你的错误信息中,因为这样会把标签名称和你的函数耦合在一起。 token.split_contents()[0] 总会是 是你的标签的名称,即使标签没有参数。

§ 这个函数返回一个 CurrentTimeNode (稍后我们将创建它),它包含了节点需要知道的关于这个标签的全部信息。在这个例子中,它只是传递了参数 "%Y-%m-%d %I:%M %p" 。模板标签开头和结尾的引号使用 format_string[1:-1] 除去。

§ 模板标签编译函数 必须 返回一个 Node 子类,返回其它值都是错的。

编写模板节点

编写自定义标签的第二步就是定义一个拥有 render() 方法的 Node 子类。继续前面的例子,我们需要定义 CurrentTimeNode :

  import datetime



  class CurrentTimeNode(template.Node):



      def __init__(self, format_string):

          self.format_string = format_string



      def render(self, context):

          now = datetime.datetime.now()

          return now.strftime(self.format_string)

这两个函数( init 和 render )与模板处理中的两步(编译与渲染)直接对应。这样,初始化函数仅仅需要存储后面要用到的格式字符串,而 render() 函数才做真正的工作。

与模板过滤器一样,这些渲染函数应该捕获错误,而不是抛出错误。模板标签只能在编译的时候才能抛出错误。

注册标签

最后,你需要用你的模块 Library 实例注册这个标签。注册自定义标签与注册自定义过滤器非常类似(如前文所述)。实例化一个 template.Library 实例然后调用它的 tag() 方法。例如:

  register.tag('current_time', do_current_time)

tag() 方法需要两个参数:

模板标签的名字(字符串)。如果被遗漏的话,将会使用编译函数的名字。

1

编译函数。

和注册过滤器类似,也可以在Python2.4及其以上版本中使用 register.tag 修饰:

  @register.tag(name="current_time")

  def do_current_time(parser, token):

      # ...



  @register.tag

  def shout(parser, token):

      # ...

如果你像在第二个例子中那样忽略 name 参数的话,Django会使用函数名称作为标签名称。

在上下文中设置变量

前一节的例子只是简单的返回一个值。很多时候设置一个模板变量而非返回值也很有用。那样,模板作者就只能使用你的模板标签所设置的变量。

要在上下文中设置变量,在 render() 函数的context对象上使用字典赋值。这里是一个修改过的 CurrentTimeNode ,其中设定了一个模板变量 current_time ,并没有返回它:

  class CurrentTimeNode2(template.Node):



      def __init__(self, format_string):

          self.format_string = format_string



      def render(self, context):

          now = datetime.datetime.now()

          context['current_time'] = now.strftime(self.format_string)

          return ''

注意 render() 返回了一个空字符串。 render() 应当总是返回一个字符串,所以如果模板标签只是要设置变量, render() 就应该返回一个空字符串。

你应该这样使用这个新版本的标签:

  {% current_time2 "%Y-%M-%d %I:%M %p" %}

  <p>The time is {{ current_time }}.</p>

但是 CurrentTimeNode2 有一个问题: 变量名 currenttime 是硬编码的。这意味着你必须确定你的模板在其它任何地方都不使用{{ currenttime }} ,因为 {% current_time2 %} 会盲目的覆盖该变量的值。

一种更简洁的方案是由模板标签来指定需要设定的变量的名称,就像这样:

  {% get_current_time "%Y-%M-%d %I:%M %p" as my_current_time %}

  <p>The current time is {{ my_current_time }}.</p>

为此,你需要重构编译函数和 Node 类,如下所示:

  import re



  class CurrentTimeNode3(template.Node):



      def __init__(self, format_string, var_name):

          self.format_string = format_string

          self.var_name = var_name



      def render(self, context):

          now = datetime.datetime.now()

          context[self.var_name] = now.strftime(self.format_string)

          return ''



  def do_current_time(parser, token):

      # This version uses a regular expression to parse tag contents.

      try:

          # Splitting by None == splitting by spaces.

          tag_name, arg = token.contents.split(None, 1)

      except ValueError:

          msg = '%r tag requires arguments' % token.contents[0]

          raise template.TemplateSyntaxError(msg)



      m = re.search(r'(.*?) as (w+)', arg)

      if m:

          fmt, var_name = m.groups()

      else:

          msg = '%r tag had invalid arguments' % tag_name

          raise template.TemplateSyntaxError(msg)



      if not (fmt[0] == fmt[-1] and fmt[0] in ('"', "'")):

          msg = "%r tag's argument should be in quotes" % tag_name

          raise template.TemplateSyntaxError(msg)



      return CurrentTimeNode3(fmt[1:-1], var_name)

      



  

相关教程