Python Web开发Django框架模板应用指导

内容导读

  1. 模板与模板引擎
  2. 模板查找顺序
  3. 模板语言
  4. 模板继承
  5. Jinja2引擎

一、模板与模板引擎

Django的模板文件是一个文本文件,这个文件可以是任何类型的文本(如HTML、CSV等),但通常保存为HTML类型,最后渲染为前端网页效果,类似于Java中的模板引擎Thymeleaf。

Django项目通过模板引擎解释模板文件,一个Django项目中可以配置一个或多个模板引擎。Django有内置的模板引擎,也支持广泛使用的Python模板引擎Jinja2。

若要在Django项目中使用模板,需先在settings.py文件的TEMPLATES选项中配置模板引擎。TEMPLATES的值是一个列表,列表的每个元素对应一个引擎。

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            …
        },
    },
]

在配置模板引擎时通常需要为其指定四项信息:BACKEND、DIRS、APP_DIRS、OPTIONS。

常用OPTIONS配置项:

Django定义了标准API,无论使用哪个模板引擎,Django总以标准API加载和渲染模板。加载模板的标准API为get_template()和selsect_template(),它们定义在django.templates.loader模块中。

语法格式:

get_template(template_name, using = None)
select_template(template_name_list, using = None)

get_template()接收一个模板名,返回Template对象;select_template()接收一个模板名称列表,按顺序尝试加载列表中的模板,返回找到的第一个模板的Template对象。get_template()和select_template()的参数using是可选参数,接收模板引擎的名字,用于限定搜索时使用的模板引擎。

get_template()和select_template()生成的Template对象包含一个render()方法,该方法用于渲染模板。

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

context:用于接收一个字典,字典内容为将要嵌入到模板中的上下文,可以为空;

request:一个HttpRequest对象。

二、模板查找顺序

了解模板查找顺序有助于模板文件的组织与管理工作,本节将结合模板引擎配置,说明Django项目中模板文件的查找顺序。

假设Django项目的模板引擎配置代码如下:

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

使用get_template()方法加载模板,为其传入参数“story_detail.html”,模板引擎查找模板的顺序如下。

(1) /home/html/example.com/story_detail.html(Django引擎)。
(2) /home/html/default/story_detail.html(Django引擎)。
(3) /home/html/jinja2/story_detail.html(Jinja2引擎)。

使用select_template()方法加载模板,为其传入参数“['story_253_detail.html', 'story_detail.html']”,模板引擎查找模板查找顺序如下。

(1) /home/html/example.com/story_253_detail.html(Django引擎)。
(2) /home/html/default/story_253_detail.html(Django引擎)。
(3) /home/html/jinja2/story_253_detail.html(Jinja2引擎)。
(4) /home/html/example.com/story_detail.html(Django引擎)。
(5) /home/html/default/story_detail.html(Django引擎)。
(6) /home/html/jinja2/story_detail.html(Jinja2引擎)。

render_to_string()

django.shortcuts模块中定义了一个用于实现模板加载和渲染的快捷方式——render_to_string()。

render_to_string(template_name,context = None,request = None,using = None)

render_to_string()函数像get_template()一样加载模板,并立刻调用Templates对象的render()方法。

三、模板语言

3.1 变量

模板文件包含静态的HTML文本和描述数据如何插入HTML文本中的动态内容,模板系统的模板语言分变量(variables)和标签(tags)两部分定义了这些动态内容的语法,此外,Django为变量定义了过滤器(filter),也支持自定义过滤器,以便模板可以呈现更加灵活的数据。

模板变量用于标识模板中会动态变化的数据,当模板被渲染时,模板引擎将其替换为视图中传递而来的真实数据。

{{ variable }}

说明:模板变量名由字母、数字和下画线(“_”)组成,但不能以下画线开头。

模板语言通过点字符(“.”)进一步访问变量中的数据,但由于模板不明确模板变量的类型,因此模板引擎会按以下顺序进行尝试。

需要注意的是,若点字符后是一个方法,这个方法在调用时不带括号。

3.2 过滤器

过滤器用于过滤变量,获取更精确的数据,其语法格式如下:

{{ variables|filters }}

使用多个管道符号(“|”)连接多个过滤器,连续对同一变量进行过滤,其语法格式如下:

{{ variables|filters1|filters2... }}

需要注意的是,管道符号和变量、过滤器之间不能有空格。

一些过滤器可以接收参数,过滤器与参数之间使用“:”分隔。若参数中含有空格,参数必须放在引号之内,例如{{ list|join:", " }}。

常用的模板过滤器:

示例如下:

3.3 标签

标签蕴含一定的逻辑,它的功能要比变量复杂,例如一些标签用于输出文本;一些标签通过执行循环或逻辑控制流;一些标签加载外部信息到模板中,以供后续变量的使用。

标签格式简单,例如:{% tag %};也有一些标签必须成对出现,以标识模板文本的开始和结束

格式如下:

{% tag %}
   ...
{% endtag %}

常用的模板标签:

1、 for

模板语言中for的用法与Python中的for相同。使用for标签遍历书单book_list,并输出所有书名,示例代码如下:

{% for book in book_list %}
    <li>{{ book.name }}</li>
{% endfor %}

模板中的for支持反向遍历列表,语法格式如下。

{% for obj in list reversed %}

若要遍历双层列表,可以解包内层列表中的每个元素到多个变量中。

{% for x, y in points %}
    There is a point at {{ x }},{{ y }}
{% endfor %}

遍历双层列表的操作在遍历字典时同样适用。示例代码如下:

{% for key, value in data.items %}
    {{ key }}: {{ value }}
{% endfor %}

注意:操作符“.”查找优先于方法查找,因此若字典中包含键items,data.items将返回data[‘items’]而非data.items()。

Django为for循环定义了一些变量,具体如下表。

2、for...empty

for循环可以使用可选的{%empty%}子句,若给定的数组为空或无法找到,则显示{%empty%}子句的文本。

{% for book in book_list %}
    <li>{{ book.title }}</li>
{% empty %}
    <li>抱歉,图书列表为空</li>
{% endfor %}

3、if/elif/else

if/elif/else是条件判断标签,与Python中的if、elif、else含义相同,若条件为True,显示相应子句中的内容。

{% if book_list %}
    现有闲余图书: {{ book_list|length }}本
{% elif book_leased_list %}
    图书待归还……
{% else %}
    没有图书
{% endif %}

if标签允许使用逻辑运算符:and、or或not进行布尔测试,允许同一标签中同时使用and和or,and的优先级高于or。

{% if athlete_list and coach_list or cheerleader_list %}

相当于是:

if (athlete_list and coach_list) or cheerleader_list

注意:模板语言的if标签不支持括号,若需明确表示混合语句中子句的优先级,应使用if嵌套语句。

if标签还支持运算符==、!=、<、>、<=、>=以及in、not in、is、is not。

{% if somevar == "x" %}
  This appears if variable somevar equals the string "x"
{% endif %}

4、include

在使用include标签时需为其传入置于单/双引号中的变量或硬编码字符串,或指示模板的变量。

{% include "foo/bar.html" %}
{% include template_name %}

include标签可以在加载模板的同时利用关键字with为模板传递变量。

{% include "name_snippet.html" with person="Jane" greeting="Hello" %}

若只希望使用提供的变量(或不使用变量)渲染上下文,需使用only选项。

{% include "name_snippet.html" with greeting="Hi" only %}

5、load

load标签用于加载自定义模板标签和过滤器。

{% load somelibrary package.otherlibrary %}

6、from

from标签用于从库中加载部分标签和过滤器。

{% load foo bar from somelibrary %}

7、now

now标签用于显示当前日期时间,可以使用一些格式控制字符对显示的内容进行格式化。

It is {% now "jS F Y H:i" %}

8、url

url标签用于返回与给定视图和可选参数匹配的绝对路径(不带域名的URL)。

url可以有多个参数,参数之间以空格分隔,其中第一个参数为URL模式名称,可以是带引号的字符串或任何其他上下文变量;

其余参数是要传递给URL的可选参数。示例如下:

{% url 'some-url-name' v1 v2 %}

以上代码为URL模式some-url-name传递了两个位置参数,也可以以关键字形式为URL模式传参,示例代码如下。跟上一样啊

{% url 'some-url-name' v1 v2 %}

9、autoescape

autoescape标签用于控制当前的自动转义行为,参数有on和off两个取值。当自动转义生效时,所有变量内容都会在将结果放入输出之前(但在应用任何过滤器之后)应用HTML转义。

{% autoescape on %}
    {{ body }}
{% endautoescape %}

10、block

block标签用于定义可由子模板覆盖的块。

{% block 模块名 %}
...模块内容...
{% endblock %}

11、comment

comment标签用于添加注释,模板引擎会忽略{% comment %}与{% endcomment %}之间的所有内容,标签可以嵌套使用。

12、cycle

cycle标签定义了一组参数,每次遇到该标签时模板引擎都会提取其中的一个参数,第一次遇到时提取第一个参数,第二次遇到时提取第二个参数,依此类推。

当所有参数提取完毕时,标记将循环到第一个参数并再次进行提取。

{% for o in some_list %}
    <tr class="{% cycle 'row1' 'row2' %}">
        ...
    </tr>
{% endfor %}

13、extends

extends标签标记当前模板所继承的模板。该标签有以下两种使用形式。

{% extends "base.html" %}
{% extends variable %}

14、firstof

firstof标签用于输出变量中第一个非False参数,若所有参数都为False则不进行输出。

{% firstof var1 var2 var3 %}

相当于:

{% if var1 %}
    {{ var1 }}
{% elif var2 %}
    {{ var2 }}
{% elif var3 %}
    {{ var3 }}
{% endif %}

15、with

with标签用于为复杂变量创建别名,此操作在多次使用高消耗方法(如与数据库相关的方法)时非常有用。

{% with total=business.employees.count %}
    …
{% endwith %}

以上使用total作为business.employees.count的别名。

3.4 自定义过滤器和标签

自定义的过滤器和标签通常位于应用目录中的包templatetags之下,你可以在templatetags包中创建文件,在文件中自定义过滤器和标签。

说明:这个文件将被load标签加载到模板中,应避免文件名与其他应用的标签和过滤器重名。

为应用booklist添加自定义过滤器,假设过滤器位于文件filters.py中,此时该应用的目录结构如下:

booklist\
    __init__.py
    models.py
    templatetags\
        __init__.py
        filters.py
    views.py
    ...

使用load标签加载filters.py文件,示例如下:

{% load filters %}

包含自定义标签的应用需被注册到INSTALLED_APPS中,以便load标签工作。Django不限制templatetags目录下模块文件的数量。

注意:load标签的参数filters指代模块名而非应用名。

为了使自定义过滤器和标签生效,templatetags下的过滤器文件必须包含模块级变量register,如下所示:

from django import template
register = template.Library()

register变量是一个template.Library实例,该变量需在模块顶部定义。

说明:

模板系统旨在展示内容,而非规定程序逻辑,自定义过滤器与标签时也应尽量遵循此宗旨。

下面将分别介绍如何自定义过滤器和标签,以及如何使用自定义的过滤器和标签。

1、自定义过滤器

自定义过滤器是一些包含一到两个参数的Python函数,这些函数有以下两个特点:

接收的变量值不局限于字符串。

参数可以有默认值,也可以省略。

在应用目录下新建包templatetags,在其中新建文件filiters.py,在该文件中自定义过滤器

def sum(value, arg):
return value + arg

说明:若过滤器不接收参数,可省略函数参数arg。

自定义过滤器需要注册到Library实例中,以便在Django模板中使用。示例代码如下:

register.filter('sum',sum)

也可以利用register.filter()装饰器注册过滤器,示例如下:

@register.filter(name='examp')
def examp(value, arg):
	return value + arg

若过滤器与Python函数同名,filter()方法的参数可以省略,示例如下:

@register.filter
def examp(value, arg):
	return value + arg

在模板中使用自定义过滤器examp,示例如下:

{{ data|examp:3 }}

说明:

模板语言不提供异常处理,模板引发的任何异常都将作为服务器错误被抛出。

若有合理的返回值,过滤器函数应避免引发异常。

2、自定义标签

标签可以实现任何功能,因此自定义标签比自定义过滤器更加复杂。Django提供了许多快捷方式,以便开发者编写标签。

simple_tag()是django.template.Library的一个方法,它接收一个包含任意个参数的函数,将其包装后注册到模板系统中。

import datetime
from django import template
register = template.Library()
@register.simple_tag
def current_time(format_string):
    return datetime.datetime.now().strftime(format_string)

若标签需要访问当前模板的上下文,可以在注册标签时使用参数takes_context。

@register.simple_tag(takes_context=True)
def current_time(context, format_string):
    timezone = context['timezone']
    # get_current_time()是自定义的获取当前时间的方法
    return get_current_time(timezone, format_string)

注意:此时标签函数的第一个参数必须是context,只要在自定义标签时设置装饰器参数takes_context=True,模板便会自动从上下文获取参数。

模板标签的另一种常见的功能是在当前模板中渲染另一个模板来呈现一些数据,这类标签被称为包含标签(inclusion tags)。

假设定义一个返回给定列表对象obj的标签show_results,该标签在模板中的用法如下:

{% show_results obj %}

假设obj的值为['First choice', 'Second choice', 'Third choice'],则此条语句的输出为:

<ul>
  <li>First choice</li>
  <li>Second choice</li>
  <li>Third choice</li>
</ul>

定义标签show_results,示例代码如下:

def show_results(obj):
    # 根据当前对象获取所有选择集合
    choices = obj.choice_set.all()	
    return {'choices': choices}

编写一个用于呈现标签输出内容的模板,这个模板是标签固定功能的一部分,示例代码如下:

<ul>
{% for choice in choices %}
    <li> {{ choice }} </li>
{% endfor %}
</ul>

调用Library的inclusion_tag()方法注册标签show_results。假设前面的模板代码存储在模板目录下的文件results.html中,将标签与模板关联,示例如下:

@register.inclusion_tag('results.html')
def show_results(obj):
    ...

至此,一个实现自定义的包含标签实现完毕。

四、模板继承

Django模板实现了模板继承机制。模板继承机制允许开发人员先在一个模板中定义多个页面共有的内容和样式,再以该模板为基础拓展模板。

模板继承机制使用模板系统中的block标签和extends标签实现,其中block标签标识与继承机制相关的代码块,extends指定子模板所继承的模板。子模板可以通过继承获取父模板中的内容

模板继承示例:

父模板和子模板在浏览器中呈现的效果分别如下图所示:

五、Jinja2引擎

Jinja2是一个使用Python实现的模板引擎,它的设计思想源于Django的模板引擎,但它拓展了Django模板语法,实现了一系列强大的功能,并具有比Django默认引擎更高的效率(Jinja2宣称比Django默认引擎快10-20倍)。

Jinja2功能齐全、速度快,同时得到了Django的良好支持,所以Jinja2在Django开发中也得到了广泛应用。

1、配置Jinja2模板引擎

配置Jinja2模板之前需先安装Jinja2模板。使用pip工具快速安装Jinja2,具体命令如下:

# 全局安装
py -m pip install jinja2
# 或,在python环境目录下使用
pip install jinja2

安装完成后,打开Django配置文件settings.py,在TEMPLATES选项中添加Jinja2模板引擎。

示例代码如下:

{	
	'BACKEND': 'django.template.backends.jinja2.Jinja2',
	'DIRS': [os.path.join(BASE_DIR, 'templates')], 
	'APP_DIRS': True,
	'OPTIONS': {
		'context_processors': [             ……	],
	},
},

2、使用Jinja2

Jinja2模板的用法与Django模板相同,语法元素和元素的功能也十分相似,但它们之间也存在一些区别。

(1)方法调用

Django模板中对方法的调用是隐式的,例如:

{% for page in user.get_created_pages() %}
    ...
{% endfor %}

(2)过滤器参数

Jinja2使用括号包含过滤器参数,例如:

{{ items|join(', ',’arg’) }}

(3)循环

Jinja2的循环与Django循环十分相似,区别在于Jinja2中的循环变量为loop而非forloop。

(4)cycle

Jinja2中没有cycle标签,但它通过loop变量的cycle()方法隐式实现了cycle标签的功能。

{% for user in users %}
    <li class="{{ loop.cycle('odd', 'even') }}">{{ user }}</li>
{% endfor %}

Jinja2的语法与Django模板的语法有很高的匹配度,但不可在Jinja2环境中直接使用Django模板。

另外需注意:Jinja2的扩展接口与Django的有根本区别, Django自定义标签无法在Jinja2环境下正常工作。

更多精彩内容请关注本站!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值