第五章 模板


本章,我们会讨论以下议题:

1. Django模板语法的特性
2. 组织模板
3. Bootstrap
4. 模板继承树模式
5. 活动连接模式

理解Django的模板语言特点

是时候谈谈MTV模板三件套中的第三个东西了。而你的团队或许有关心模板设计的设计者。或者你想要自己设计模板。无论选择哪种方式,你都需要对它们非常熟悉。毕竟,模板是直接面向用户的。

我们对Django模板语言特性进行快速入门。

变量

每个模板都获取一组上下文变量。类似于Python的字符串format()方法的单大括号{variable}语法,Django使用双大括号{{ variable }}语法。我们来看看它们之间的比较:

• 在纯净的Python中语法为`<h1>{title}</h1>`。例如:  
    >>> "<h1>{title}</h1>".format(title="SuperBook") '<h1>SuperBook</h1>'
• Django模板中的相等的语法是 `<h1>{{ title }}</h1>`.

• 如下传递下相同的上下文产生同样的结果:  
       >>> from django.template import Template, Context
       >>> Template("<h1>{{ title }}</h1>").render(Context({"title":
       "SuperBook"}))
       '<h1>SuperBook</h1>'

属性

点号在Django模板中是多用途的运算符。这里有三种不同类型的运算符,属性查找,字典查找,列表索引查找(依照顺序)。

• Python中我们首先,定义上下文变量和类:  
       >>> class DrOct:
               arms = 4
               def speak(self):
                   return "You have a train to catch."
       >>> mydict = {"key":"value"}
       >>> mylist = [10, 20, 30]
    我们来看看三种查询类型的Python语法:  
       >>> "Dr. Oct has {0} arms and says: {1}".format(DrOct().arms,
       DrOct().speak())
       'Dr. Oct has 4 arms and says: You have a train to catch.'
       >>> mydict["key"]
        'value'
       >>> mylist[1]
        20
• Django的模板等于下面:
       Dr. Oct has {{ s.arms }} arms and says: {{ s.speak }}
       {{ mydict.key }}
       {{ mylist.1 }}
注释

注意speak方法没有接受参数,除了被当作属性的self之外。

过滤器

某些时候,变量是需要修改的。从根本上来说,你要利用这些变量去调用函数。Django使用类似于Unix过滤器的管道语法{{ var|method1|method2:"tag" }},而不是var.method().method2(arg)这样的链式函数调用。

过滤器的另外一个限制是它不能够访问模板上下文。过滤器仅在数据传递到过滤器,以及过滤器的参数时才有效。因此,在模板上下文中它主要用来更改变量。

  • 在Python中运行以下命令:
    >>> title="SuperBook"
    >>> title.upper()[:5]
    'SUPER'
    
  • 其对应的Django模板为:
    {{ title|upper|slice:':5' }}
    

标签

编程语言能够做的事情不仅仅是显示变量。Django的模板语言多中相似的语法形式,比如iffor。它们应该用{% if %}这样的语法写成。多个针对模板的形式,比如includeblock也都是用标签语法写成的。

• 在Python中运行以下命令:  
       >>> if 1==1:
       ...     print(" Date is {0} ".format(time.strftime("%d-%m-%Y")))
        Date is 31-08-2014
• 其对应的Django模板形式如下:  
       {% if 1 == 1 %} Date is {% now 'd-m-Y' %} {% endif %}

哲学——不要去发明一种编程语言

新手们间的常见问题是如何执行发现模板中百分比这样的数字计算。从设计哲学的角度来说,模板系统无意支持这样做:

  • 对变量赋值
  • 高级逻辑

该决定能够阻止你在模板中添加业务逻辑。根据使用PHP或者类ASP语言的经历,将逻辑和表现层混合到一起是一场后期维护的噩梦。不过,你可以编写自定义的模板标签(很快就会学到)以执行任何计算,特别是和表现层相关的计算。

提示

最佳实践 保证业务逻辑远离模板。

组织模板

由startproject命令创建的默认项目布局并没有定义好的模板的位置。这个问题解决起来也非常简单。在项目的根目录中创建一个名称为templates的目录。然后在settings.py目录中添加TEMPLATE_DIRS变量。

译者注 在Django 1.8之后的版本中,已经不用手动定义模板路径。startproject命令创建的默认配置文件已经自动包含了项目根目录下的templates目录。

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',
            ],
        },
    },
]
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')]

要定义的就这么多。例如,你可以添加一个称做`about.html`的模板,然后在`urls.py`文件中引用它:  

```python
urlpatterns = patterns('',url(r'^about/$', TemplateView.as_view(template_name='about.html'),name='about'),

模板也可以定义在应用中。在应用的目录中创建模板目录对存储应用专用的模板来说是非常理想的。

下面是组织模板的一些优秀实践:

  • 再一个单独的目录中,保证所有app专用的模板都放在这个app的模板目录汇总,例如,projroot/app/templates/app/template
  • 对模板使用.html这样对扩展名。
  • 对模板添加一个下划线前缀,表示将要继承使用对代码片段,例如,_navbar.html
    - 将应用专用的模板放到一个独立的应用的模板目录中,例如,projecroot/app/templates/app/template, 而html也能够理解app两次出现在路径的原因。
  • 为模板使用.html格式的文件扩展名。
  • 对模板使用带下划线的前缀,该模板是能够在继承中使用的代码片段,例如,_navbar.html

对其他模板语言的支持

从Django1.8起多模板引擎将得到支持。届时会存在内建的Django模板语言(我们之前谈到的普通模板语言)和Jinja2的支持。在很多的性能基准测试中,Jinja2比Django模板快了很多。

对于指定的模板引擎和所有模板相关的设置来说都需要存在额外的TEMPLATES设置。而TEMPLATE_DIRS设置很快也会被移除。

欧女士

For the first time in weeks, Steve's office corner was bustling with frenetic activity. With more recruits, the now five-member team comprised of Brad, Evan, Jacob, Sue, and Steve. Like a superhero team, their abilities were deep and amazingly well-balanced.

Brad and Evan were the coding gurus. While Evan was obsessed over details, Brad was the big-picture guy. Jacob's talent in finding corner cases made him perfect for testing. Sue was in charge of marketing and design.

In fact, the entire design was supposed to be done by an avant-garde design agency. It took them a month to produce an abstract, vivid, color-splashed concept loved by the management. It took them another two weeks to produce an HTML-ready version from their Photoshop mockups. However, it was eventually discarded as it proved to be sluggish and awkward on mobile devices.

Disappointed by the failure of what was now widely dubbed as the "unicorn vomit" design, Steve felt stuck. Hart had phoned him quite concerned about the lack of any visible progress to show management. In a grim tone, he reminded Steve, "We have already eaten up the project's buffer time. We cannot afford any last-minute surprises."

It was then that Sue, who had been unusually quiet since she joined, mentioned that she had been working on a mockup using Twitter's Bootstrap. Sue was the growth hacker in the team—a keen coder and a creative marketer.

She admitted having just rudimentary HTML skills. However, her mockup was surprisingly thorough and looked familiar to users of other contemporary social networks. Most importantly, it was responsive and worked perfectly on every device from tablets to mobiles.

The management unanimously agreed on Sue's design, except for someone named Madame O. One Friday afternoon, she stormed into Sue's cabin and began questioning everything from the background color to the size of the mouse cursor. Sue tried to explain to her with surprising poise and calm.

An hour later, when Steve decided to intervene, Madame O was arguing why the profile pictures must be in a circle rather than square. "But a site-wide change like that will never get over in time," he said. Madame O shifted her gaze to him and gave him a sly smile. Suddenly, Steve felt a wave of happiness and hope surge within him. It felt immensely reliving and stimulating. He heard himself happily agreeing to all she wanted.

Later, Steve learnt that Madame Optimism was a minor mentalist who could influence prone minds. His team loved to bring up the latter fact on the slightest occasion.

使用Bootstrap

对任何人来说开始从零建设整个网站的那些日子都是件很艰苦的事情。像推特的或者说是扎克伯格基金会的Bootstrap这样的CSS框架,栅格系统,极好的排列板式以及预先设置好的风格,下手好起点。使用Bootstrap的大多数网页设计都对移动设备显示友好。

图片:略

我们将使用Bootstrap,而且步骤也类似于其他的CSS框架。在网站中使用Bootstrap有三种方法:

  • Find a project skeleton: If you have not yet started your project, then finding a project skeleton that already has Bootstrap is a great option. A project skeleton such as edge (created by yours truly) can be used as the initial structure while running startproject as follows:

  • 寻找项目架构:如果还没有启动项目,那么查找项目架构

$ django-admin.py startproject --template=https://github.com/arocks/edge/archive/master.zip --extension=py,md,html myproj

可选择的是,你可以使用其中一个拥有Bootstrap支持的cookiecutter模板。

复制包含css、js的dist目录,以及静态目录下根目录中的字体目录。保证这个路径设置在settings.py中的STATICFILES_DIRS:

       STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
    Now you can include the Bootstrap assets in your templates, as follows:
       {% load staticfiles %}
         <head>
           <link href="{% static 'css/bootstrap.min.css' %}"
       rel="stylesheet">

怎么它们看上去都一个模样啊!

Bootstrap或许是快速开发的非常好的选择。不过,有时候,开发者懒得去改变默认的外观。这就留给了浏览网站的用户一个糟糕的映像,他们发现你的网站的外观也太熟悉了而且很无趣。

Bootstrap带来了很多的改进外观需求的选项。如下,有一个称作 variables.less 的文件包含了来自默认字体的主要种类色彩中的多个变量:

    @brand-primary: #428bca;
    @brand-success: #5cb85c;
    @brand-info: #5bc0de;
    @brand-warning: #f0ad4e;
    @brand-danger: #d9534f;
    @font-family-sans-serif:  "Helvetica Neue", Helvetica, Arial, sans-
    serif;
    @font-family-serif:
    @font-family-monospace:
    monospace;
    @font-family-base:
    Georgia, "Times New Roman", Times, serif;
    Menlo, Monaco, Consolas, "Courier New",
    @font-family-sans-serif;

Bootstrap文档解释了你该如何设置构造系统(包含LESS编译器在内),以便讲这些文件编译为样式表。另外一个很方便的选择是你去访问Bootstrap往后在哪的“定制”区域以在线生成定制的样式表。

要感谢大量的有关Bootstrap的社区,这里有很多网站,比如bootswatch.com,该站点拥有可以替换bootstrap.min.css的主题化样式表。

另外一个方法是重写Bootstrap版式。如果发现再不同版本的Bootstrap之间升级自定义的Bootstrap样式是多么无聊的事情,因为这只是个建议而已。在这个方法中,你可以在独立的CSS(或者LESS)文件中添加自己的全站样式,然后在标准的Bootstrap样式表中继承它。这样,你就能够以对全站样式表最小改变来简单的升级Bootstrap文件。

最后然后却不是最少的,你可以让自己CSS类更为有意义以替换结构式的名称,比如“row”或者'column-md-4', 和 'wrapper' 或者 'sidebar'。如下,你可以用几行LESS代码完成它:

   .wrapper {
     .make-row();
    }
    .sidebar {
     .make-md-column(4);
   }

这是处理一个功能的可行称作mixin的方法(听着很耳熟,是吧!)。使用LESS源码你可以完全依照自身需求完成定制。

模板模式

Django模板语言非常的简单。然而,它通过一些简洁的模板设计模式可以让你节省很多的时间。让我们来看看其中的一些模板设计模式。

模式-模板继承树

问题:在多个页面中模板存在很多重复的内容。

解决方案: 只要是有可能的地方就使用模板继承,并在其他地方继承代码片段。

问题细节

用户期望网站的页面能够遵循结构的一致性。某些接口元素,比如在很多web应用都能够见到的导航栏菜单,首部,和页脚。不过,在每一个模板中都重复它们显示相当笨重。

大多数的模板语言都拥有模板继承机制。其他文件的内容,也可以是一个模板都能够在它们被调用的地方导入。在大型项目中这样做会让你感到乏味的。

在每一个模板中将要继承的代码片段序列很大程度上是相同的。倒入顺序很重要,而错误检查却很难。理论上,我们能够创建一个基础结构。新的页面应当扩展这个基础模板

方案详情

Django模板拥有一个强大的扩展机制。类似于编程中的类,模板可以通过继承来扩展。不过,要让模板工作起来,base自身必要像下面这样组织到block中。

图片:略

The base.html template is, by convention, the base structure for the entire site. This template will usually be well-formed HTML (that is, with a preamble and matching closing tags) that has several placeholders marked with the {% block tags %} tag. For example, a minimal base.html file looks like the following:

我们约定,base.html 模板是真个网站的基本结构。这个模板

    <html>
    <body>
    <h1>{% block heading %}Untitled{% endblock %}</h1>
    {% block content %}
    {% endblock %}
    </body>
    </html>

这里存在两个能够被重写块,heading和content。你可以扩展基础模板以创建能够重写这些块的特有页面。例如,这里是一个about页面:

    {% extends "base.html" %}
    {% block content %}
    <p> This is a simple About page </p>
    {% endblock %}
    {% block heading %}About{% endblock %}

注意,我们不需要出现重复的结构。我们按照任意顺次饮用block。渲染结果将和定义在base.html的内容一样在正确的地方使用正确的block。

如果继承的模板没有重写block,那么它的父模板内容可以被使用。在前面的例子中,假如about模板不存在头部,那么它将拥有默认的'Untitled'头部。

The inheriting template can be further inherited forming an inheritance chain. This pattern can be used to create a common derived base for pages with a certain layout, for example, single-column layout. A common base template can also be created for a section of the site, for example, blog pages.

通常,所有的继承链都可以通过一个公共的根,base.html来回溯;因此,样式的名称——模板继承树。当然,这样的做法不需要到什么技巧的。而错误页面404.html和500.html通常并不需要继承,并去掉了大多数的标签以阻止更多错误的发生。

模式-活动链接

问题:在很多对页面中导航栏是一个常见对组件。然而,活动链接需要在用户登陆时进行响应的映射。

解决方案:根据情况不同,通过设置上下文变量或者请求路径来改变活动链接的外观。

问题细节

在导航栏内实现活动链接的简单办法是手动地在每个一个需要实现该功能的页面中设置。不过,这样过既不符合DRY原则,也并不能保证万无一失。

方案详情

确定活动链接的解决方法有多种。包括,基于Javascript的方法,它们主要是将纯模板和自定义标签组合到一起。

临时模板方案

在继承导航模板的代码片段时可以饮用一个称作active_link的变量,这个方案实现起来既简单又容易。

在每一个模板中,你都需要包括以下内容(或者继承它):

    {% include "_navbar.html" with active_link='link2' %}

_navbar.html文件包含了拥有一组检查活动链接的变量的导航菜单:

    {# _navbar.html #}
   <ul class="nav nav-pills">
     <li{% if active_link == "link1" %} class="active"{% endif %}><a
   href="{% url 'link1' %}">Link 1</a></li>
     <li{% if active_link == "link2" %} class="active"{% endif %}><a
   href="{% url 'link2' %}">Link 2</a></li>
     <li{% if active_link == "link3" %} class="active"{% endif %}><a
   href="{% url 'link3' %}">Link 3</a></li>
   </ul>

自定义标签

Django模板提供了一组多功能的内奸标签。创建自己自定义标签很简单。因此自定义标签是使用在应用内部的,那么就需要在应用的内部创建一个称作templatetags到目录。这个目录必须是一个Python包,所以应该包含一个(空的)__init__.py文件。

接下来,在给定名称的Python文件中编写自己的自定义模板。例如,对于激活链接样式,我们可以使用一下内容创建一个称作nav.py的文件:

    # app/templatetags/nav.py
    from django.core.urlresolvers import resolve
    from django.template import Library

    register = Library()

 @register.simple_tag
    def active_nav(request, url):
       url_name = resolve(request.path).url_name
       if url_name == url:
           return "active"
       return ""

这个文件定义了一个称作active_nav的自定义标签。它从request参数重新取回URL的路径组件(即,第四章——视图与路由,其中有对URL路径对详细说明)。然后resolve()函数用来从路径查询URL的样式名称(定义在urls.py中)。最后,让仅在样式名称匹配期望的样式名称时反悔字符串“active”。

在模板中调用这个自定义标签的语法是{% active_nav request 'pattern_name' %}。注意,在用到这个标签的每个地方都需要将request传递进去。

在多个视图中使用一个变量显得太笨拙了。如下,我们在settings.py中添加一个内建的上下文处理器到TEMPLATE_CONTEXT_PROCESSORS,request便可以用request变量的身份出现在整个网站了:

     # settings.py
    from django.conf import global_settings

    TEMPLATE_CONTEXT_PROCESSORS = \
       global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
           'django.core.context_processors.request',
        )

现在,在模板中剩下的内容要使用这个定制标签,这样就可以设置激活属性了:

    {# base.html #}
   {% load nav %}
   <ul class="nav nav-pills">
     <li class={% active_nav request 'active1' %}><a href="{% url
   'active1' %}">Active 1</a></li>
     <li class={% active_nav request 'active2' %}><a href="{% url
   'active2' %}">Active 2</a></li>
     <li class={% active_nav request 'active3' %}><a href="{% url
   'active3' %}">Active 3</a></li>
   </ul>

总结

在这一章,我们浏览了Django的模板特性。因为在Django中切换模板语言很简单,所以很多人都考虑替换。不过,在浏览其他的选择之前重要的是学些内建模板语言的设计哲学。

下一章,我们会学习Django的杀手级功能,即,admin接口,以及我们如何定制它。

powered by Gitbook 该教程制作时间: 2016-04-15 11:07:18