模板层 Template

每一个 Web 框架都需要一种很便利的方法用于动态生成 HTML 页面。 最常见的做法是使用模板。

模板包含所需 HTML 页面的静态部分,以及一些特殊的模版语法,用于将动态内容插入静态部分。

说白了,模板层就是如何往 HTML 文件中填入动态内容的系统。

Django 可以配置一个或多个模板引擎(语言),也可以不用引擎。

Django 自带一个称为 DTL(Django Template Language )的模板语言,以及另外一种流行的 Jinja2 语言(需要提前安装,pip install Jinja2)。

Django 为加载和渲染模板定义了一套标准的 API,与具体的后台无关。加载指的是,根据给定的模版名称找到模板然后预处理,通常会将它编译好放在内存中。渲染则表示,使用 Context 数据对模板插值并返回生成的字符串。

DTL 作为 Django 原生的模板系统,一直到 Django1.8,都是唯一的内置模板系统。如果没有特别重要的理由,需要选择另外一种模板系统的话,建议坚持使用 DTL。

Django 很多内部组件都使用了 DTL,例如 django.contrib.admin,如果你不想让它们罢工,或者花费大力气进行修改,不要放弃 DTL。

一、 配置引擎

模板引擎通过 settings 中的 TEMPLATES 设置来配置。这是一个列表,与引擎一一对应,每个元素都是一个引擎配置字典。由 startproject 命令生成的settings.py 会自定定义如下的值:

TEMPLATES = [
  {
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [],
    'APP_DIRS': True,
    'OPTIONS': {
      # ... some options here ...
    },
  },
]

BACKEND:后端。

内置的后端有 django.template.backends.django.DjangoTemplates 和django.template.backends.jinja2.Jinja2。

OPTIONS 中包含了具体的后端设置。

由于绝大多数引擎都是从文件加载模板的,所以每种模板引擎都包含两项通用设置:

  1.DIRS:定义了一个目录列表,模板引擎按列表顺序搜索这些目录以查找模板源文件。

  2.APP_DIRS:告诉模板引擎是否应该进入每个已安装的应用中查找模板。通常请将该选项保持为 True。

每种模板引擎后端都定义了一个惯用的名称作为应用内部存放模板的子目录名称。(例如 Django 为它自己的模板引擎指定的是 ‘templates’,为 jinja2 指定的名字是‘jinja2’)。尤其是,django 允许你有多个模板引擎后端实例,且每个实例有不同的配置选项。 在这种情况下你必须为每个配置指定一个唯一的 NAME .

DTL 引擎的 OPTIONS 配置项中接受以下参数:

  1.'autoescape':一个布尔值,用于控制是否启用 HTML 自动转义功能。默认为 True。

  2.context_processors: 以"."为分隔符的 Python 调用路径的列表。默认是个空列表。

  3.'debug':打开/关闭模板调试模式的布尔值。默认和 setting 中的 DEBUG 有相同的值。

  4.'loaders':模板加载器类的虚拟 Python 路径列表。默认值取决于 DIRS 和APP_DIRS 的值。

  5.string_if_invalid:非法变量时输出的字符串。默认为空字符串。

  6.file_charset:用于读取磁盘上的模板文件的字符集编码。默认为 FILE_CHARSET的值。

  7.'libraries':用于注册模板引擎。 这可以用于添加新的库或为现有库添加备用标签。

  8.'builtins':以圆点分隔的 Python 路径的列表。

二、 简单的用法

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

get_template(template_name,using = None)

该函数使用给定的名称查找和加载模板,并返回一个 Template 对象。

模板的查找和加载机制取决于每种后端引擎和配置,如果想使用指定的模板引擎进行查找,请将模板引擎的 NAME 赋给 get_template 的 using 参数。

select_template(template_name_list,using = None)

和 get_template()相似, 只不过它使用包含模板名称的列表作为参数。

由 select_template()和 get_template()返回的 Template 对象都必须提供一个 render()方法,如下所示:

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

通过给定的 context 对该模板进行渲染。

如果提供了 context,那么它必须是一个 dict 对象。如果要提供 request 参数 ,必须使用 HttpRequest 对象。

针对下面的 TEMPLATES 配置,对模版文件的搜索顺序和路径如下:

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'引擎)

/home/html/default/story_detail.html('django'引擎)

/home/html/jinja2/story_detail.html('jinja2'引擎)

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

/home/html/example.com/story_253_detail.html('django'引擎)

/home/html/default/story_253_detail.html('django'引擎)

/home/html/jinja2/story_253_detail.html('jinja2'引擎)

/home/html/example.com/story_detail.html('django'引擎)

/home/html/default/story_detail.html('django'引擎)

/home/html/jinja2/story_detail.html('jinja2'引擎)

注意:Django 查找到任何一个匹配的模板后便停止搜寻,所以这是个类似 url 搜索的短路操作!

强调:前面我们介绍过,建议在每个 APP 的的模版子目录下都建立一个子目录来唯一对应这个 APP。这样做可以增强你的 APP 的可用性。 将所有的模版文件放在根模版目录下会引发混淆。

要在一个子目录内加载模板,像下面这样:

get_template('news/story_detail.html')

如果结合上面例子中的 TEMPLATES 配置,这将会尝试按顺序查找并加载下列模板︰

/home/html/example.com/news/story_detail.html('django'引擎)

/home/html/default/news/story_detail.html('django'引擎)

/home/html/jinja2/news/story_detail.html('jinja2'引擎)

另外,为了减少加载模板、渲染模板等重复工作,django 提供了处理这些工作的快捷函数。

render_to_string(template_name, context=None, request=None,using=None)[source]

render_to_string()会像 get_template()一样加载模板并立即调用 render()方法。 它需要以下参数。

  1.TEMPLATE_NAME:要加载的模板的名称或列表。

  2.context:要用作模板的上下文进行渲染的数据字典,也就是你要插入的动态数据字典。

  3.request:可选的 HttpRequest 对象。

  4.using:指定使用的模板引擎 NAME。 搜索模板将仅限于该引擎。

用法示例:

from django.template.loader import render_to_string

rendered = render_to_string('my_template.html', {'foo': 'bar'})

三、基本语法

Django 模板语言(DTL)的语法包括四种结构。

1. 变量

变量的值来自 context 中的数据字典, 类似于字典对象的 keys 到 values 的映射关系。

变量是被}}和{{括起来的部分,例如:

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

当模版引擎遇到一个变量,它将从上下文 context 中获取这个变量的值,然后用值替换掉它本身。假如有一个上下文{'first_name': 'John', 'last_name': 'Doe'},模板渲染后的真实值为:

My first name is John. My last name is Doe.

变量的命名包括任何字母数字以及下划线("_")的组合。点(".")也有可能会在变量名中出现,不过它有特殊的含义。最重要的是,变量名称中不能有空格或标点符号。

当模版系统遇到点("."),它将以这样的顺序查询这个圆点具体代表的功能:

  1.字典查询(Dictionary lookup)

  2.属性或方法查询(Attribute or method lookup)

  3.数字索引查询(Numeric index lookup)

如果你使用的变量不存在,模版系统将插入 string_if_invalid 选项的值,默认设置为''(空字符串)。

2. 标签

模版语言中的标签类似 Python 中的函数,功能多样,使用灵活。可以输出内容、控制结构,甚至可以访问其他的模板标签。

标签是由%}和{%来定义的,例如:

{% csrf_token %} # csrf 令牌标签

大部分标签都接受参数:

{% cycle 'odd' 'even' %} # 循环使用'odd'和'even'

部分标签需要使用起始和闭合标签,典型代表为 for 循环标签和 if 判断标签:

{% if user.is_authenticated %}Hello, {{ user.username }}.{% endif %}

Django 自带了大约 24 个内置的模版标签。下面是一些常用的标签:

2.1. for 循环标签

循环对象中每个元素。需要结束标签{% endfor %} 。例如,显示 athlete_list中提供的运动员列表:

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

2.2. if,elif 和 else 标签

计算一个表达式,并且当表达式的值是“True”时,显示块中的内容。需要{%endif %}结束标签。整体逻辑非常类似 Python 的 if、elif 和 else,如下所示。:

{% if athlete_list  %}
  Number of athletes:  {{ athlete_list|length  }}
{% elif athlete_in_locker_room_list  %}
  Athletes should be out of the locker room soon!
{% else  %}
  No athletes.
{% endif  %}

在上面的例子中,如果 athlete_list 不是空的,运动员的数量将显示为{{ athlete_list|length }}。否则,如果 athlete_in_locker_room_list 不为空,将显示“Athletes should be out…”。如果两个列表都是空的,将显示“No athletes.” 。

还可以在 if 标签中使用过滤器和多种运算符:

{% if athlete_list|length > 1 %}
  Team: {% for athlete in athlete_list %} ... {% endfor %}
{% else %}
  Athlete: {{ athlete_list.0.name }}
{% endif %}

需要注意,大多数模版过滤器都返回字符串类型,所以使用过滤器做整数类型的比较通常是错误的,但 length 是一个例外。

2.3. block 和 extends 标签

继承和复写模版。类似 Python 的类继承和重写机制。

3. 过滤器

过滤器看起来是这样的:{{ name|lower }}。使用管道符号(|)来应用过滤器。该过滤器将文本转换成小写。

过滤器可以“链接”。一个过滤器的输出应用于下一个过滤器。例如:

{{ text|escape|linebreaks }}就是一个常用的过滤器链,它首先转移文本内容,然后把文本行转成<p>标签。

一些过滤器带有参数。 过滤器的参数看起来像是这样: {{ bio|truncatewords:30 }}。这将显示 bio 变量的前 30 个词。

过滤器参数包含空格的话,必须用引号包起来。例如,使用逗号和空格去连接一个列表中的元素,你需要使用{{ list|join:", " }}。

Django 提供了大约六十个内置的模版过滤器,很多时候你想要的功能,它都已经提供了,有需要的时候就去查一下吧。下面是一些常用的模版过滤器:

3.1. default

为 false 或者空变量提供默认值,像这样:

{{ value|default:"nothing" }}

3.2. length

返回值的长度。它对字符串和列表都起作用。

{{ value|length }}

如果 value 是['a', 'b', 'c', 'd'],那么输出 4。

3.3. filesizeformat

格式化为“人类可读”文件大小单位(即'13 KB',4.1 MB','102 bytes'等)。

{{ value|filesizeformat }}

如果 value 是 123456789,输出将会是 117.7MB。

4. 注释

模版语言的注释看起来像这样:

{# this won't be rendered #} # 单行注释

{% comment %}标签提供多行注释功能。

附:

Django 内置标签总览

可以查询下表来总览 Django 的内置标签:

标签 说明
autoescape 自动转义开关
block 块引用
comment 注释
csrf_token CSRF 令牌
cycle 循环对象的值
debug 调试模式
extends 继承模版
filter 过滤功能
firstof 输出第一个不为 False 的参数
for 循环对象
for … empty 带 empty 说明的循环
if 条件判断
ifequal 如果等于
ifnotequal 如果不等于
ifchanged 如果有变化,则..
include 导入子模版的内容
load 加载标签和过滤器
lorem 生成无用的废话
now 当前时间
regroup 根据对象重组集合
resetcycle 重置循环
spaceless 去除空白
templatetag 转义模版标签符号
url 获取 url 字符串
verbatim 禁用模版引擎
widthratio 宽度比例
with 上下文变量管理器

Django 内置过滤器总览

可以查询下表来总览 Django 的内置过滤器:

过滤器   说明
add 加法
addslashes 添加斜杠
capfirst 首字母大写
center 文本居中
cut 切除字符
date 日期格式化
default 设置默认值
default_if_none 为 None 设置默认值
dictsort 字典排序
dictsortreversed 字典反向排序
divisibleby 整除判断
escape 转义
escapejs 转义 js 代码
filesizeformat 文件尺寸人性化显示
first 第一个元素
floatformat 浮点数格式化
force_escape 强制立刻转义
get_digit 获取数字
iriencode 转换 IRI
join 字符列表链接
last 最后一个
length 长度
length_is 长度等于
linebreaks 行转换
linebreaksbr 行转换
linenumbers 行号
ljust 左对齐
lower 小写
make_list 分割成字符列表
phone2numeric 电话号码
pluralize 复数形式
pprint 调试
random 随机获取
rjust 右对齐
safe 安全确认
safeseq 列表安全确认
slice 切片
slugify 转换成 ASCII
stringformat 字符串格式化
striptags 去除 HTML 中的标签
time 时间格式化
timesince 从何时开始
timeuntil 到何时多久
title 所有单词首字母大写
truncatechars 截断字符
truncatechars_html 截断字符
truncatewords 截断单词
truncatewords_html 截断单词
unordered_list 无序列表
upper 大写
urlencode 转义 url
urlize url 转成可点击的链接
urlizetrunc urlize 的截断方式
wordcount 单词计数
wordwrap 单词包裹
yesno 将 True,False 和 None,映射成字符串‘yes’,‘no’,‘maybe’

四、模板继承

Django 模版引擎中最强大也是最复杂的部分就是模版继承了。模版继承允许你创建一个包含基本“骨架”的父模版,它包含站点中的共有元素,并且可以定义能够被子模版覆盖的 blocks。

通过下面这个例子,理解模版继承的概念:

<html lang="en">
<head>
  <link rel="stylesheet" href="style.css" />
  <title>{% block title %}My amazing site{% endblock title%}</title>
</head>
<body>
  <div id="sidebar">
    {% block sidebar %}
    <ul>
      <li><a href="/">Home</a></li>
      <li><a href="/blog/">Blog</a></li>
    </ul>
    {% endblock sidebar %}
  </div>
  <div id="content">
    {% block content %}{% endblock content%}
  </div>
</body>
</html>

这个模版,通常被命名为 base.html,它定义了一个可以用于两列排版页面的简单HTML 骨架。

“子模版”需要做的是先继承父模板 base.html,然后复写、填充,或者说实现其中的blocks。

block 是在子模版中可能会被覆盖掉的位置。在上面的例子中,block 标签定义了三个可以被子模版内容填充的 block,分别是 title、content 和 siderbar。

子模版可能看起来是这样的:

{% extends "base.html" %}
{% block title %}My amazing blog{% endblock title%}
{% block content %}
{% for entry in blog_entries %}
  <h2>{{ entry.title }}</h2>
  <p>{{ entry.body }}</p>
{% endfor %}
{% endblock content%}

extends 标签是这里的关键。它告诉模版引擎,这个模版“继承”了另一个模版。当模版系统处理这个模版时,首先会去加载父模版,也就是“base.html”。

加载过程中,模版引擎将注意到 base.html 中的三个 block 标签,并用子模版中的内容来替换这些 block。 根据 blog_entries 的值,最终输出可能看起来是这样的:

<html lang="en">
<head>
  <link rel="stylesheet" href="style.css" />
  <title>My amazing blog</title>
</head>
<body>
  <div id="sidebar">
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/blog/">Blog</a></li>
  </ul>
  </div>
  <div id="content">
    <h2>Entry one</h2>
    <p>This is my first entry.</p>
    <h2>Entry two</h2>
    <p>This is my second entry.</p>
  </div>
</body>
</html>

请注意,上面例子中的子模版并没有定义 sidebar block,这种情况下,将使用父模版中的内容。父模版的{% block %}标签中的内容总是被用作默认内容。

下面是使用继承的一些相关说明

  1.如果在模版中使用{% extends %}标签,它必须是模版中的第一个标签,必须放在文件首行!

  2.在 base 模版中设置越多的{% block %}标签越好。子模版不必定义全部父模版中的 blocks,所以可以在大多数 blocks 中填充合理的默认内容,然后,只定义你需要的那一个。

  3.如果发现你自己在复制大量重复的模版内容,那意味着你应该把重复的内容移动到父模版中的一个{% block %}中。

  4.如果需要获取父模板中的 block 的内容,可以使用{{ block.super }}变量。如果想要在父 block 中新增内容而不是完全覆盖它,这将非常有用。

  5.在{% block %}之外创建的变量使用模板标签的 as 语法,不能在块内使用。

例如,下面的模板不会显示任何内容:

{% trans "Title" as title %}

{% block content %}{{ title }}{% endblock %}

1.为了更好的可读性,可以给{% endblock %}标签一个名字,

像这样:{% block content %} ... {%endblock content %}

在大型模版中,这有助于你清楚的看到哪一个{% block %}标签被关闭了。

2.最后,请注意不能在一个模版中定义多个相同名字的 block 标签

1.创建一个 base.html 模版,用来控制整个站点的主要视觉和体验。

2.为站点的每一个 app,创建一个 base_SECTIONNAME.html 模版。 例如base_user.html,base_goods.html。这些模版都继承 base.html,并且包含了各自特有的样式和设计。

3.为每一个页面类型,创建独立的模版。 这些模版继承对应 app 的模版。上面的方式可以使代码得到最大程度的复用,并且使得添加内容到共享的内容区域更加简单,例如 app 范围内的导航条。

 

五、CSRF

CSRF(Cross-site request forgery)跨站请求伪造,是一种常见的网络攻击手段。

Django 为我们提供了防范 CSRF 攻击的机制。

1、基本使用

默认情况下,使用 django-admin startproject xxx 命令创建工程时,CSRF 防御机制就已经开启了。如果没有开启,请在 MIDDLEWARE 设置中添加'django.middleware.csrf.CsrfViewMiddleware'。

对于 GET 请求,一般来说没有这个问题,CSRF 通常是针对 POST 方法的!

在含有 POST 表单的模板中,需要在其<form>表单元素内部添加 csrf_token 标签,如下所示:

<form action="" method="post">
  {% csrf_token %}
  ....
</form>

这样,当表单数据通过 POST 方法,发送到后台服务器的时候,除了正常的表单数据外,还会携带一个 CSRF 令牌随机字符串,用于进行 csrf 验证。如果表单中没有携带这个 csrf 令牌,会报错。

六、图片验证码

为了防止机器人频繁登录网站或者破坏分子恶意登录,很多用户登录和注册系统都提供了图形验证码功能。

在 Django 中实现图片验证码功能非常简单,有现成的第三方库可以使用,我们不必自己开发,这个库叫做 django-simple-captcha。

1、安装 captcha

执行命令:pip install django-simple-captcha

Django 自动帮我们安装了相关的依赖库 six、olefile 和 Pillow,其中的 Pillow 是大名鼎

鼎的绘图模块。

2、注册 captcha

在 settings 中,将‘captcha’注册到 app 列表里:

captcha 需要在数据库中建立自己的数据表,所以需要执行 migrate 命令生成数据表:

3、添加 url 路由

在根目录下的 urls.py 文件中增加 captcha 对应的网址:

urlpatterns = [
  ……..
  url(r'^captcha', include('captcha.urls')) # 增加这一行
]

4、修改 forms.py

如果上面都 OK 了,就可以直接在我们的 forms.py 文件中添加 CaptchaField 了。

from django import forms
from captcha.fields import CaptchaField
class UserForm(forms.Form):
  username = forms.CharField(label="用户名", max_length=128,widget=forms.TextInput(attrs={'class': 'form-control'}))
  password = forms.CharField(label="密码", max_length=256,widget=forms.PasswordInput(attrs={'class': 'form-control'}))
  captcha = CaptchaField(label='验证码')

注意需要提前导入 from captcha.fields import CaptchaField,然后就像写普通的 form字段一样添加一个 captcha 字段就可以了!

5、修改 login.html

由于我们前面是手动生成的 form 表单,所以还要修改一下,添加 captcha 的相关内容,如下所示:

<div class="form-group">
  {{ login_form.captcha.errors }}
  {{ login_form.captcha.label_tag }}
  {{ login_form.captcha }}
</div>

这里额外增加了一条{{ login_form.captcha.errors }}用于明确指示用户,你的验证码不正确。

6、查看效果

重启服务器,进入登录页面,尝试用用户名错误、密码不对、验证码不对、全对的不同情况,看看我们新增的四位验证码的效果如何。

就是这么简单!我们加入了一个防止机器人或者恶意登录的图形验证码功能,虽然界面难看了点,但底子是好的,你可以根据需要进行美化。其中验证图形码是否正确的工作都是在后台自动完成的,只需要使用 is_valid()这个 forms 内置的验证方法就一起进行了,完全不需要在视图函数中添加任何的验证代码,非常方便快捷!

猜你喜欢

转载自www.cnblogs.com/sunBinary/p/10459284.html