django-模板初探

  一般而言,我们在视图函数中处理各种业务逻辑之后,应该返回一个 HttpResponse 对象。而 HttpResponse 对象的第一个参数接受字符串或者是迭代器,作为响应报文的主体。但是这意味着我们要将 HTML 内容写到 python 的代码中,或者写到一个文件中,然后用 python 打开,将可迭代的文件对象作为第一个参数。

  这样的后果是:

  1、代码很混乱,而且实际工作中前后端一般是由不同的程序员处理的,让可能不懂 python 的前端人员在 python 文件中书写 HTML 代码是一件很危险并且效率低下的事情。

  2、内容很难做到动态改变,这就失去的动态生成页面的优势了。

  为了解决以上的问题,django引入了模板层,当然这也是所有遵循MVC框架都有的。在1.8版本之前,django只支持内置的django模板引擎,简称为 DTL;在1.8以后,django还加入了对 jinja2 的支持,但是,如果你要使用jinja2模板引擎的话,还需要自己去下载jinja2的库( pip install Jinja2 ),因为django只提供了接口的支持。另外,如何你还有使用其他的第三方引擎,可能要进行 后端BACKEND)的自定义,因为django只提供了 DTL 和jinja2的后端支持。

  接下来来看看如何配置使用。

  模板的配置是通过 setting.py 中的 TEMPLATES 变量进行的,下面是通过 startproject 命令生成的项目中(1.9版本),在 setting 中的默认配置:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        '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',
            ],
        },
    },
]

 

  首先,TEMPLATES 是一个列表,列表中的每个元素都是一个字典,每个字典代表一个模板引擎。上面的就是默认的配置,代表只使用 django 自带的 DTL 模板引擎,当然如果你有需要同时使用多个模板引擎的话,可以这样配置:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            '/home/html/example.com',
            '/home/html/default',
        ],
    },
    {
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        'DIRS': [
            '/home/html/jinja2',
        ],
    },
]

  这样就可以根据需要使用不同的引擎了,一般通过函数的 using 参数来指定引擎的 NAME。关于这两个参数的含义下面会进行深入的解释。

  接下来看看要怎么配置:

  TEMPLATES:默认是一个空列表,列表中的每个元素都是字典,每个字典代表一个模板引擎的配置。一般我们使用 startproject 命令来生成项目时,这里都会自动填充为第一个例子所示的内容,当然因版本的不同会有差异。如果你不打算使用模板的话,也可以把里面的元素删掉,只留一个空列表。

  BACKEND:后端,是字典中的一个键,当然接下来的所有设置都位于字典中,下面就不一一说明强调了。没有默认值。这里应该是一个字符串,字符串的内容是可调用的 python 路径,也就是说如果在 python 中 import 的时候,应该能够找到。django支持的后端都位于 django.template.backends 目录下,例如:

  它们是模块中的一个类,这个类继承于 BaseEngine :

  更深层次的东西这里就不继续讨论了。

  NAME:模板引擎的别名,通常会在各种函数的 using 中用到。默认为定义后端(backends)的模块的名称,由上图可知,django的模板引擎的别名就是‘django’;jiaja2模板引擎的别名就是‘jinja2’。

  DIRS:搜索模板的路径顺序,模板引擎将按照里面的顺序来搜索模板文件。默认为空列表。注意:这里的路径应该是django可以访问的路径,因为我们的服务器一般运行在linux平台中,而linux平台对于权限的管理非常严格。一般我们的django的通过 wsgi 运行在 web 服务器后面时,如Apache、nginx等,服务器的权限是什么,django的权限就是什么。

    APP_DIRS:默认为False,但是 startproject 命令会为我们设置为 True。告诉模板引擎是否应该进入每个已安装的应用中查找模板。每种模板引擎后端都定义了一个固定的名称作为应用内存放模板的子目录的名称。例如,django的就叫做‘templates’ ;jinja2的就是‘jinja2’。

  OPTIONS:传递额外的参数给后端引擎,表示开启某些功能。可用的参数随着模板引擎的不同而不同。默认为一个空字典,当然 startproject 命令为我们填充了一些参数。

 下面是一些旧版本中的特性,在1.8之前,并不是所以的设置都在 TEMPLATES 变量中的,通常会分开设置:

TEMPLATE_CONTEXT_PROCESSORS:

默认:

["django.contrib.auth.context_processors.auth",
"django.template.context_processors.debug",
"django.template.context_processors.i18n",
"django.template.context_processors.media",
"django.template.context_processors.static",
"django.template.context_processors.tz",
"django.contrib.messages.context_processors.messages"]

 

  在1.8中是一个元祖,在1.9中是一个列表,不过里面的内容是一样的。在1.8版本中已经废弃了,现在应该在 OPTIONS 中,使用 'context_processors' 来代替,就像文章一开始的那个例子那样。当然这里针对的是 django 模板引擎。另外,在1.8版本开始,内置的模板上下文处理器从 django.core.context_processors 移动到 django.template.context_processors 中。

  这是一个列表,列表里面的给个元素应该是一个字符串,字符串的内容是一个可调用的对象,它们被用来在请求上下文(RequestContext)中填充特定的内容。这些可调用的对象将获得一个 request 对象作为参数,同时返回一个含有成员的字典,这个字典中的成员将被用来填充内容。

下面是其中的一个函数:

def static(request):
    """
    Adds static-related context variables to the context.
    """
    return {'STATIC_URL': settings.STATIC_URL}

 

 

TEMPLATE_DEBUG:

默认:False

  在1.8版本开始抛弃,改为在  OPTIONS 字典中设置 'debug' 键及其值。

  这是一个布尔值,表示是否开启模板的 debug 模式。如果为 True ,那么在模板渲染的时候触发任何异常时,将显示一个详细的错误页面,错误页面中包含模板的相关代码,并会用适当的方式强度错误的地方。

  只有在为 True 的时候才会显示这个页面,所以在开发阶段可以开启以便调试。

  另外,DEBUG 设置也有类似的功能。

TEMPLATE_DIRS

在1.9中默认为一个空列表,在1.8中为一个空元祖。

  这是一个表示模板文件的路径的容器,当你调用  django.template.loaders.filesystem.Loader 函数时,将会按照里面的顺序搜索模板文件。

  注意,这些路径应该使用Unix风格的正斜杠(/),即使在Windows中运行。

  当然,在1.8以后,由 DIRS 代替了,也就是开头的几个例子中的那样。

TEMPLATE_LOADERS

默认:

['django.template.loaders.filesystem.Loader',
 'django.template.loaders.app_directories.Loader']

 

  在1.9中是一个列表,在1.8中是一个元祖,里面的内容倒是没有变。

  另外,在1.8版本以后,应该在 OPTIONS 字典中使用'loaders' 键作为代替。

  容器中的每个元素应该是个字符串,每个字符串是一个可调用的python路径,所指代的是用于加载模板所使用的类。每个类都知道如何从特定源导入模板。另外,可以使用一个元祖来代替字符串,元祖中的第一个元素是 Loader 所在的模块,剩下的元素传递给 Loader 用于初始化的。

  注意:这里可以看出 DIRS 中的路径查找比 app 目录中的优先。

 

TEMPLATE_STRING_IF_INVALID

默认为:''(空字符串)

  在1.8版本之后,应该在 OPTIONS 字典中使用 'string_if_invalid' 键作为替代。

  当模板中使用的变量是无效的(例如拼写错误)时,用来代替输出的字符串。

 

'file_charset '

  是 OPTIONS 字典中的一个键,其值表示从磁盘中读取模板文件时所用的字符集,默认为 FILE_CHARSET 的值(‘utf-8’),而 FILE_CHARSET 是管理从磁盘中打开文件时所用的字符集,包括模板文件和初始SQL数据文件。

 

下面两个是1.9中新增的:

'libraries'

  它的值是一个字典,字典中包含了标签和模板模块路径,路径用Python的点路径表示,用于在模板引擎中注册它们。这可以用来添加新的库或为现有的库提供备用标签。

例如:

OPTIONS={
    # 其他设置
    'libraries': {
        'myapp_tags': 'path.to.myapp.tags',
        'admin.urls': 'django.contrib.admin.templatetags.admin_urls',
    },
    # 其他设置
}

  可以通过将相应的字典键传递到 {% load %} 标签,以加载库。例如: {% load 'myapp_tags' %}

 

'builtins'

  一个列表,列表中的元素是一个python点路径的字符串,用来从模板标签模块中添加 built-ins(内建)的标签。

例如:

OPTIONS={
    'builtins': ['myapp.builtins'],
}

  内建的标签和过滤器可以在不调用{% load %} 标签的情况下调用。

 


 

加载模板

  django.template.loader 定义了两个函数以加载模板

1. get_template(template_name[, dirs][, using]) 

  该函数使用给定的名称加载模板并返回一个Template 对象.(该名称是字符串类型,可以是文件名称,也可以是包含路径分隔符 / 的名称)

  所获得的返回值并不是  django.template.Template 的实例,而是后端模板引擎所定义的 Template 对象。

  get_template()会尝试获取从所有的路径中获取模板。如果模板不能成功找到,将会抛出 TemplateDoesNotExist如果能够找到模板但是包含非法值,将会抛出 TemplateSyntaxError.

  模板的查找和加载机制取决于每种后台引擎和配置。

  template_name:模板的名称,django会根据配置用这个名字去搜索指定的模板。

例如,当配置为下面所示时:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            '/home/html/example.com',
            '/home/html/default',
        ],
    },
    {
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        'DIRS': [
            '/home/html/jinja2',
        ],
    },
]

如果你调用函数get_template('story_detail.html'), Django按以下顺序查找story_detail.html:

  • /home/html/example.com/story_detail.html ('django' engine)
  • /home/html/default/story_detail.html ('django' engine)
  • /home/html/jinja2/story_detail.html ('jinja2' engine)

  会先用第一个引擎,然后再使用第二个引擎的配置进行搜索,当找到第一个符合的文件时,就会停止搜索,也就是后面也有同名的文件的话,将会‘失明’。

  另外,我们可以使用包括路径的名称,django也推荐这样做,最好的方法就是为每一个app建立一个同名的目录,然后在该目录下放置这个app要用到的模板,这样可以避免重名问题带来的模板查找错误,例如: get_template('news/story_detail.html') 

  • /home/html/example.com/news/story_detail.html ('django' engine)
  • /home/html/default/news/story_detail.html ('django' engine)
  • /home/html/jinja2/news/story_detail.html ('jinja2' engine)

   dirs :该参数从1.8起被移除,这里不再讨论。

   using:表示要使用哪个引擎进行查找,该参数接受的是模板引擎的 NAME ,详情参考上面的配置中的 NAME。默认为 None ,如上面的例子所示,为 None 时将使用两个引擎进行查找,两个引擎按配置的先后进行查找,如果指定了使用某个引擎,另一个引擎就不会再查找了。

 

2. select_template(template_name_list[, dirs][, using]) 

  select_template() 和 get_template()很相似, 只不过它用了一个模板名称的列表作为参数。按顺序搜索模板名称列表内的模板并返回第一个存在的模板。

例如:如果你调用函数 select_template(['story_253_detail.html', 'story_detail.html']), Django按以下顺序查找:

  • /home/html/example.com/story_253_detail.html ('django' engine)
  • /home/html/default/story_253_detail.html ('django' engine)
  • /home/html/jinja2/story_253_detail.html ('jinja2' engine)
  • /home/html/example.com/story_detail.html ('django' engine)
  • /home/html/default/story_detail.html ('django' engine)
  • /home/html/jinja2/story_detail.html ('jinja2' engine)

  其他的参数都是一样的,这里不重复说明。

  你可以通过 select_template() 来实现更为灵活的模板加载。例如: select_template(['story_%s_detail.html' %story.id, 'story_detail.html'])的形式。

 


 

渲染

  按照约定,查找到的模板,也就是调用上面两个搜索模板的函数后,将返回一个由后端模板引擎所定义的 Template 对象,该对象必须实现 render()方法,所以在自定义模板引擎的时候要要注意这一点。

 Template.render(context=None, request=None) 

  context:它必须是一个python字典对象(dict),将用这个字典对模板进行渲染,如果没有提供,将使用一个空的字典。

  request:它必须是一个 Httprequest 对象,后端引擎必须妥善处理它,同时还要处理 CSRF 令牌,当然如何实现是后端引擎做的事,也取决于不同的后端引擎。

 render_to_string(template_name[, context][, context_instance][, request][, using]) 

  也是位于 django.template.loader 中,用于将模板渲染后返回字符串。

  template_name:模板名称,如果是一个字符串,将调用 get_template() 方法;如果是一个列表将调用 select_template() 方法。

  context:一个python字典,用来渲染模板。

  context_instance:从1.8版本起废弃,这里不再讨论。

  request:必须是一个 HttpRequest 对象,并且在整个模版渲染期都是可用的。

  using:用来指定查找模板的引擎。

 


 

快捷函数

  一般而言我们可以将渲染后的字符串作为 HttpResponse 的第一个参数,作为构建响应报文的主体。

  由于这个动作实在太常有了:加载--渲染--返回;所以django提供了两个快捷函数来处理这些事务。

  这两个位于 django.shortcuts 模块中,在使用前记得先导入。

 render(request, template_name[, context][, context_instance][, content_type][, status][, current_app][, dirs][, using]) 

  这个函数实现查找,加载,渲染,构建 HttpResponse 对象一整套流程,所以我们可以使用它来节省许多功夫。

  request:用于生成 response 对象的 request 对象。

  template_name:模板名称,或者是包含许多模板名称的列表。

  context:渲染使用的python字典。

  context_instance:1.8版本起废弃,这里不再讨论。

  content_type:指定 MIME 类型,默认为 DEFAULT_CONTENT_TYPE  的值。

  status:响应的状态码,默认为200.

  current_app:指示哪个应用包含当前的视图。1.8版本后废弃,现在要设置 request.current_app 进行代替。

  using:用于加载模板使用的模板引擎的名称

例子:

from django.shortcuts import render

def my_view(request):
    # View code here...
    return render(request, 'myapp/index.html', {"foo": "bar"},
        content_type="application/xhtml+xml")

这个示例等同于:

from django.http import HttpResponse
from django.template import RequestContext, loader

def my_view(request):
    # View code here...
    t = loader.get_template('myapp/index.html')
    c = RequestContext(request, {'foo': 'bar'})
    return HttpResponse(t.render(c),
        content_type="application/xhtml+xml")

 

 

2. render_to_response(template_name[, context][, context_instance][, content_type][, status][, dirs][, using]) 

  参数和上面的一样,就是少了 request 参数。

例子:

from django.shortcuts import render_to_response

def my_view(request):
    # View code here...
    return render_to_response('myapp/index.html', {"foo": "bar"},
        content_type="application/xhtml+xml")

这个示例等同于:

from django.http import HttpResponse
from django.template import Context, loader

def my_view(request):
    # View code here...
    t = loader.get_template('myapp/index.html')
    c = Context({'foo': 'bar'})
    return HttpResponse(t.render(c),
        content_type="application/xhtml+xml")

 

 

  在这里,我们看到了两个没有见过的函数:Context();RequestContext()。并且渲染的时候使用的是这两个函数的返回值,并不是标准的python字典。这和官方文档的说法貌似有点矛盾,其原文是这样的:If context is provided, it must be a dict. If it isn’t provided, the engine will render the template with an empty context.

  而我初步看了这两个类的代码后,认为其应该是对于python字典的一种扩展,所以称其为 dict 也还是说得过去的。不过当我们使用快捷函数的时候,所传递的依然是python的标准字典,因为快捷函数内部做了转换的处理,关于这两个函数,等我研究一番后在说。

  另外,这里要补充一点,当开启了 CSRF 中间件后,并处理 POST 的表单时,请使用 render 函数,而不是 render_to_response,只有 render 函数会处理 csrf token,否则你可能会面对 csrf 的报错。

 

posted @ 2016-07-06 00:43  scolia  阅读(2687)  评论(0编辑  收藏  举报