Django 探秘(1) 模板语言

概念:模板语言由块和变量组成

  1. BLOCK(块):由 "{%" 和 "%}" 包含构成,可以有循环(loop)和分支(if)构成
  2. VARIABLE(变量):由 "{{" 和 "}}" 包含,可以由英文字母,数字,下划线和点号组成
  3. CONTEXT(上下文): 是一个从"VARIABLE NAME" 到 "VARIABLE VALUE"的映射
  4. RENDER(渲染):是把模板里面的VARIABLE NAME 通过CONTEXT对象替换成VARIABLE VALUE

 

使用模板由如下两步达到:

1.编译裸的模板代码到模板对象里,最简单的生成模板对象的方法是

从django.template.Template对象里得到,通常如下:

>>> from django.template import Template
>>> t = Template("My name is {{ my_name }}.")
>>> print t
<django.template.Template instance>

2.调用模板对象的render()函数渲染,其中render的参数是Context对象

接1中的代码,如下代码渲染

>>> c = Context({"my_name": "Adrian"})
>>> t.render(c)
"My name is Adrian."
>>> c = Context({"my_name": "Dolores"})
>>> t.render(c)

"My name is Dolores."

其中"."在模板代码中的解释是以如下顺序进行解释:

1) 字典查询:如foo["bar"]

>>> from django.template import Context, Template
>>> t = Template("My name is {{ person.first_name }}.")
>>> d = {"person": {"first_name": "Joe", "last_name": "Johnson"}}
>>> t.render(Context(d))
"My name is Joe."

2) 属性查询:如foo.bar

>>> class PersonClass: pass
>>> p = PersonClass()
>>> p.first_name = "Ron"
>>> p.last_name = "Nasty"
>>> t.render(Context({"person": p}))
"My name is Ron."

3) 方法调用:如foo.bar()

>>> class PersonClass2:
        ... def first_name(self):
            ... return "Samantha"
>>> p = PersonClass2()
>>> t.render(Context({"person": p}))
"My name is Samantha."

三点需要注意:

1.如果在方法匹配中,一个方法生成了一个异常,这个异常将会不断扩散,除非这个异常本身含有如下属性:silent_variable_failure,并且它的属性值为True,这时这个被匹配的变量的值为空串。

    其中django.core.exceptions.ObjectDoesNotExist是Django数据库接口API异常DoesNotExist

    的基类,它本身具有属性silent_variable_failure = True,使用Django模板和数据库模型对象的时候,DoesNotExist异常都是空串。

    >>> t = Template("My name is {{ person.first_name }}.")
    >>> class PersonClass3:
            ... def first_name(self):
                ... raise AssertionError, "foo"
    >>> p = PersonClass3()
    >>> t.render(Context({"person": p}))
    ...
    AssertionError: foo
    >>> class SilentAssertionError(Exception):
           ... silent_variable_failure = True
    >>> class PersonClass4:
           ... def first_name(self):
               ... raise SilentAssertionError
    >>> p = PersonClass4()
    >>> t.render(Context({"person": p}))
    "My name is ."

2.方法本身的调用不能带有参数,否则系统会按照既定顺序启用下一种匹配方式:4)中的列表查询

3.有些方法有副作用,如删除操作,在数据库模型对象里面,调用delete操作会删除数据库元素,如下

模板语句:"I will now delete this valuable data. {{ data.delete }}"是有问题的,需要一些额外的措施来补救,这里需要设置一个函数自身的属性alter_data(别忘了python中函数也是对象)并把其值设置为True来避免。Django中一些动态生成的函数如delete和save等自动地使得alter_data=True。

如下例:

def sensitive_function(self):
      self.database_record.delete()
sensitive_function.alters_data = True

4) 列表索引查询:如foo[bar]

>>> t = Template("The first stooge in the list is {{ stooges.0 }}.")
>>> c = Context({"stooges": ["Larry", "Curly", "Moe"]})
>>> t.render(c)
"The first stooge in the list is Larry."

模板语法

模板与一般的python语言不一样,不具有严格的程序逻辑性质,这是由设计的初衷来决定的:模板提供表达的呈现方式,而不是提供编程逻辑

模板是一个text文件,它能生成任意的ASCII文件(HTML, XML, CSV, etc.),它不用XML之类的是因为它需要对付XML方式的文档,如email,javascript,CSV等

模板由变量和标签组成,前者表示数据,可以被值替换;后者主要是控制模板的逻辑。

模板变量可以被上下文对象替换,如果替换或者"."查找失败,则以空串代替

 

Filter(过滤器)是可以对变量的值进行某种含义的更改的功能,使用"|",作用类似管道,可以多个过滤器连接,数据内容从左往右依次过滤,如{{ text|escape|linebreaks }};另外,某些filter含有参数,如

{{ bio|truncatewords:30 }},{{ list|join:", " }} ,django大概有30多个内置的filter,详细的可以参考built-in filter reference,可以构造自己的filter,参考Custom template tags and filters

 

Tags(标签),语法{% tag %},一些控制输出,一些控制逻辑流程,一些控制加载外部信息供本模块使用等等。有的标签需要有结束标签,如{% tag %} ... tag contents ... {% endtag %},django大约有20多个内置标签,具体参考built-in tag reference,常用的tag有:

1. For,例如:

<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>

2. If 和 else,例如:

{% if athlete_list %}
Number of athletes: {{ athlete_list|length }}
{% else %}
No athletes.
{% endif %}

3. Ifequal 和 ifnotequal,例如:

{% ifequal athlete.name coach.name %}
...
{% endifequal %}或者:

{% ifnotequal athlete.name "Joe" %}
...
{% endifnotequal %}

Comment,注释,例如:

{# greeting #}hello 这将注释掉“greeting”,只出现hello

{# {% if foo %}bar{% else %} #} 这将注释掉整行

如果要注释多行,需要使用 {% comment %} … {% endcomment %}

模板继承

Block和extends,在大型程序中,需要对模板进行多层的抽象和继承,免除大量代码的维护,具体如下:

  1. 在父模板里使用{% block something %}进行占位;
  2. 而在子模板一开始声明{% extends "base.html" %}
  3. 子模板对父模板对应的block标签给出各自的实现,子模板依然可以有block标签,给其子模板继承
  4. 子模板可以重载父模板上已有的block标签,如果不重载,父模板的标签依然显示出来

通常的做法,也是有效利用已有代码的做法,是:

  1. 创建一个base.html模板,这是网站的主要观感。
  2. 创建若干个base_sectionname.html模板,每一个都继承自base.html,如base_news.html,base_sports.html,这是频道的观感
  3. 为每一个网页创建各自的模板,如一篇新的文章和一个新的博客,所有的这些模板都继承自base_sectionname.html

一些需要注意的tips:

  1. 如果使用{% extends %}标签在子模板里,它必须是子模板里的第一条标签语句,否则子模板不工作
  2. 在父模板里越多的{% block %}标签越好,因为一个这样的标签就是一个hook点,在这个点上子模板可以扩充,而子模板并不需要重载每一个父模板的标签,所以即使父模板已经有实现的块,依然可以打上块标签来等待子模板继承重载
  3. 如果子模板要使用父模板的块逻辑,并且在其上加上一些自己的逻辑,而不是想重载父模板相应的block标签的话,可以使用{{ block.super }}来引用父模板的相应块,这里不再进行转义,因为父模板上已经自动转义过了
  4. 记得为每一个block标签命名,这是在大工程里比较重要的;但是不要重复,否则其模板的父模板不知道使用相同名字的哪一个子模板的块来重载父模板的块
  5. 关于转义,django默认会自动转义变量的标签,使用django对插入带标签的恶意语句是安全的,但是有时候不一定想对标签转义,这就可以使用标签{% autoescape on|off %}…{% endautoescape %}来开关

例如:

{% autoescape off %}
Hello {{ name }}
{% endautoescape %}
父模板的autoescape标签会影响到extends这个模板的子模板重载的

   6.在filter里面所有的字符串字符都不转义,使用转义的字符需要小心,

filter使用的时候,中间的间隔竖线两边不能有除了名字之外的任何字符,否则django会找不到!!

   7.使用已有的模板,可以使用{% load %}标签,如下:

{% load comments %}
{% comment_form for blogs.entries entry.id with is_public yes %}
这里load标签加载了comments库,然后利用里面的标签comment_form

也可以同时加载多个标签库,库名只需要用空格隔开即可:

{% load comments i18n %}
需要注意的是,load标签只加载库到本模板,对其父模板或者子模板均不受影响,如果它们也需要加载同样的库,需要再写一次load标签语句

使用内建的参考文档

打开admin界面,里面有需要的一些资源,如内建或加载进来的view,tag库,filter库等等资源

参考源文档 <http://docs.djangoproject.com/en/dev/topics/templates/#topics-templates>


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值