Django模板系统使用指南

Django自带的模板系统虽然比不上专业的类似jinjia2那么自由的模板系统,但是对于我们新手来说也是够用了,由于Django是一个强大且全面的大型Web框架,该有的都已经内置了,功能上不尽完美也有替代的方案,所以以后有机会再和大家说说如何替换默认自带模板系统。

今天我们来聊一聊Django模板系统使用的方法,我尽量全面且系统的介绍使用方法,如果有遗漏或欠缺的地方,麻烦指正。

1.语句语法的使用

  • 变量或常量引用:{{ 变量或常量 }}
  • 基础的语法使用:{% 语法 %}
  • 变量或常量别名:{% 旧名称 as 新名称 %}
  • 注释的简单用法:{# 注释内容 #}

1.1 静态文件引用:

<!--页头-->
{% load staticfiles %}

<!--使用-->
{% static "文件路径" %}

1.2 相对地址引用:

{% url '应用名称:url.py内定义的名字'  %}

注意:名称后面可跟着参数,传值方式为位置传参(value1,value2)和关键词传参(value="1")。

1.3 变量或常量传递:

例如:我想根据网页内容的不同显示不同的标题。
做法:

# views.py
# -*- coding: utf-8 -*-
from django.shortcuts import render
 
def index(request):
    title = "标题1"
    return render(request, 'index.html', {'title ': title })

使用:

<!--index.html-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{title}}</title>
</head>
<body>
    <h1>我的第一个HTML页面</h1>
    <p>我的第一个段落。</p>
</body>
</html>

而如果是字典呢?

# views.py
# -*- coding: utf-8 -*-
from django.shortcuts import render
 
def index(request):
    test_dict = {'name': '测试字典类型', 'content': '简单示例'}
    return render(request, 'index.html', {'test_dict': test_dict})

使用(在模板中取字典的键是用 **. **(例如:test_dict.name),而不是我们认为的Python中的 test_dict['name']):

<!--index.html-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>网站标题</title>
</head>
<body>
    <h1>{{ test_dict.name }}</h1>
    <p>{{ test_dict.content}}</p>
</body>
</html>

特别注意:所有用到上下文传递的数据只能是字典类型,模板系统并不支持下标索引。

1.4 自定义模板标签:

说明:本段参考《 追梦人物:10 - 页面侧边栏:使用自定义模板标签

有时候我们需要使用一些公共的数据,而如果每次都在视图内写获取函数,那会导致我们写很多重复代码,所以Django为我们提供了自定义模板标签。

简单示例:

首先在我们的项目应用下创建一个 templatetags 文件夹。然后在这个文件夹下创建一个** __init__.py**文件,使这个文件夹成为一个 Python 包,之后在 templatetags\ 目录下创建一个 get_data.py 文件,这个文件存放自定义的模板标签代码。

# get_data.py
from django import template

register = template.Library()

@register.simple_tag
def get_data_age():
    age = 10
    return age

使用:

<!--index.html-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>网站标题</title>
</head>
<body>
<!--导入自定义模板标签-->
{% load get_data %}
<!--将函数获取的值取名-->
{% get_data_age as age%}
    <h1>我的第一个HTML页面</h1>
        <!--使用-->
    <p>我的年纪{{ age }}</p>
</body>
</html>

说明:这里我们首先导入 template 这个模块,然后实例化了一个 template.Library 类,并将函数 get_recent_posts 装饰为 register.simple_tag。这样就可以在模板中使用语法 {% get_data_age %} 调用这个函数了。

注意: Django 1.9 后才支持 simple_tag 模板标签,如果你使用的 Django 版本小于 1.9,你将得到一个错误。

2.通用模板的使用

很多网站的网页会存在公共部分(即相同标签内容,比如导航,底部等),所以Django将这部分内容做成block,减轻我们的工作量。

2.1 简单使用方法:

首先在你的其他html文件同级目录下新建一个html文件,名称自定义,最好与公用部分含义一致,例如:test.html,下面我以这个名称做示例。

完整的HTML页面比如是这样的:

<!--index.html-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>网站标题</title>
</head>
<body>
    <h1>我的第一个HTML页面</h1>
    <p>我的第一个段落。</p>
</body>
</html>

我们把公共部分抽离出来,不同页面不同部分做成block

提示:其中name为自定义名称。

<!--test.html-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>网站标题</title>
</head>
<body>
    {% block name %}
    {% endblock name %}
</body>
</html>

然后我们就可以修改index.html的内容:

<!--index.html-->
{% extends 'test.html' %}
    {% block name %}
    {% endblock name %}

注意:“ Django 模板查找机制: Django 查找模板的过程是在每个 的 **templates **文件夹中找(而不只是当前应用 中的代码只在当前的应用的 templates 文件夹中找)。各个应用的 templates 形成一个文件夹列表,Django 遍历这个列表,一个个文件夹进行查找,当在某一个文件夹找到的时候就停止,所有的都遍历完了还找不到指定的模板的时候就是 Template Not Found (过程类似于Python找包)。这样设计有利当然也有弊,有利是的地方是一个应用可以用另一个应用的模板文件,弊是有可能会找错了。所以我们使用的时候在 templates 中建立一个 应用同名的文件夹,这样就好了。” -- 来自 《自强学堂-Django基础教程

还有一个全内容引用:
{% include 'test1.html' %} :代表导入test1.html内所有内容到指定位置(可重复使用)。

3.逻辑控制的语法

3.1 if else 条件判断:

注意:这里的if语句并没有elif。

{% if 真/假 %}
  执行语句一 
{% else %}
  否则执行语句二
{% endif%}

3.2 for in 循环遍历:

{% for 值 in 可迭代对象 %}
  执行语句一 
{% empty %}
  值为空时执行的语句
{% endfor%}

3.2.1 for in 循环内的控制变量

  • forloop.counter 索引从 1 开始算
  • forloop.counter0 索引从 0 开始算
  • forloop.revcounter 索引从最大长度到 1
  • forloop.revcounter0 索引从最大长度到 0
  • forloop.first 当遍历的元素为第一项时为真
  • forloop.last 当遍历的元素为最后一项时为真
  • forloop.parentloop 获取上一层 for 循环的 forloop

例如:

{% for item in items%}
    {{ item }}{% if not forloop.last %}如果此次循环item为最后一个值,则item后面加上本段文字{% endif %} 
{% endfor %}

其他更多用法自己实验。

3.3 ifequal/ifnotequal 值判断是否相同:

注意:{% ifequal %}比较两个值,如果相等,则显示{% ifequal %}和{% endifequal %}之间的所有内容:
{% ifnotequal%} 自然就是不相等了。

{% ifequal 值1 值2%}
  执行语句一 
{% else %}
  否则执行语句二
{% endifequal %}

4.基础的模板过滤

  • {{ bio | truncatewords:"30" }}:英文显示前30个字
  • {{ bio | truncatechars:"30"}}:显示前30个字
  • {{ pub_date | date:"F j, Y" }}:格式化
  • {{ 123|add:"5" }} :给value加上一个数值
  • {{ "AB'CD"|addslashes }} :单引号加上转义号,一般用于输出到javascript中
  • {{ "abcd"|capfirst }} :第一个字母大写
  • {{ "abcd"|center:"50" }} :输出指定长度的字符串,并把值对中
  • {{ "123spam456spam789"|cut:"spam" }} :查找删除指定字符串
  • {{ value|date:"F j, Y" }} :格式化日期
  • {{ value|default:"(N/A)" }} :值不存在,使用指定值
  • {{ value|default_if_none:"(N/A)" }} :值是None,使用指定值
  • {{ 列表变量|dictsort:"数字" }} :排序从小到大
  • {{ 列表变量|dictsortreversed:"数字" }} :排序从大到小
  • {{% if 92|divisibleby:"2" %} 判断是否整除指定数字
  • {{ string|escape }} :转换为html实体
  • {{ 21984124|filesizeformat }} :以1024为基数,计算最大值,保留1位小数,增加可读性
  • {{ list|first }} :返回列表第一个元素
  • {{ "ik23hr&jqwh"|fix_ampersands }} :&转为&
  • {{ 13.414121241|floatformat }} :保留1位小数,可为负数,几种形式
  • {{ 13.414121241|floatformat:"2" }} :保留2位小数
  • {{ 23456 |get_digit:"1" }} :从个位数开始截取指定位置的1个数字
  • {{ list|join:", " }} :用指定分隔符连接列表
  • {{ list|length }} :返回列表个数
  • {% if 列表|length_is:"3" %} 列表个数是否指定数值
  • {{ "ABCD"|linebreaks }} :用新行用<p> 、 <br /> 标记包裹
  • {{ "ABCD"|linebreaksbr }} :用新行用<br /> 标记包裹
  • {{ 变量|linenumbers }} :为变量中每一行加上行号
  • {{ "abcd"|ljust:"50" }} :把字符串在指定宽度中对左,其它用空格填充
  • {{ "ABCD"|lower }} :小写
  • {% for i in "1abc1"|make_list %}ABCDE,{% endfor %} 把字符串或数字的字符个数作为一个列表
  • {{ "abcdefghijklmnopqrstuvwxyz"|phone2numeric }} :把字符转为可以对应的数字??
  • {{ 列表或数字|pluralize }} :单词的复数形式,如列表字符串个数大于1,返回s,否则返回空串
  • {{ 列表或数字|pluralize:"es" }} :指定es
  • {{ 列表或数字|pluralize:"y,ies" }} :指定ies替换为y
  • {{ object|pprint }} :显示一个对象的值
  • {{ 列表|random }} :返回列表的随机一项
  • {{ string|removetags:"br p div" }} :删除字符串中指定html标记
  • {{ string|rjust:"50" }} :把字符串在指定宽度中对右,其它用空格填充
  • {{ 列表|slice:":2" }} :切片
  • {{ string|slugify }} :字符串中留下减号和下划线,其它符号删除,空格用减号替换
  • {{ 3|stringformat:"02i" }} :字符串格式,使用Python的字符串格式语法
  • {{ "E<A>A</A>B<C>C</C>D"|striptags }} :剥去[X]HTML语法标记
  • {{ 时间变量|time:"P" }} :日期的时间部分格式
  • {{ datetime|timesince }} :给定日期到现在过去了多少时间
  • {{ datetime|timesince:"other_datetime" }} :两日期间过去了多少时间
  • {{ datetime|timeuntil }} :给定日期到现在过去了多少时间,与上面的区别在于2日期的前后位置。
  • {{ datetime|timeuntil:"other_datetime" }} :两日期间过去了多少时间
  • {{ "abdsadf"|title }} :首字母大写
  • {{ "A B C D E F"|truncatewords:"3" }} :截取指定个数的单词
  • {{ "<a>1<a>1<a>1</a></a></a>22<a>1</a>"|truncatewords_html:"2" }} :截取指定个数的html标记,并补完整</ul>
  • {{ list|unordered_list }}</ul>多重嵌套列表展现为html的无序列表
  • {{ string|upper }} :全部大写
  • {{ link|urlencode }} :url编码
  • {{ string|urlize }} :将URLs由纯文本变为可点击的链接。(没有实验成功)
  • {{ string|urlizetrunc:"30" }} :同上,多个截取字符数。(同样没有实验成功)
  • {{ "B C D E F"|wordcount }} :单词数
  • {{ "a b c d e f g h i j k"|wordwrap:"5" }} :每指定数量的字符就插入回车符
  • {{ boolean|yesno:"Yes,No,Perhaps" }} :对三种值的返回字符串,对应是 非空,空,None

... 欢迎补充完善。

5.涉及的其他知识

5.1 自定义过滤器:

其实这里和之前1.4的自定义模板标签差不多,都是定义一个函数:

例如:这里我定义一个获取传入值前5位字符的过滤器

做法:同样是那个get_data.py文件

# get_data.py
from django import template

register = template.Library()

@register.filter(name='truncate_filter')
def truncate_chars(value):
    if value.__len__() > 5:
        return '%s......'% value[0:5]
    else:
        return value

如果没有使用name参数,django默认会将函数名作为name参数的值,所以下面的代码和上面的代码作用相同。

# get_data.py
from django import template

register = template.Library()

@register.filter
def truncate_chars(value):
    if value.__len__() > 5:
        return '%s......'% value[0:5]
    else:
        return value

说明:Library.filter(name,function,is_safe=False,needs_autoescape=False,excepts_localtime=False)函数默认需要两个参数,name是装饰器的名称(字符串类型),function是函数名。

后面三个参数可以参考 官方文档。这部分内容过于繁杂,一篇文章可能说不完,我暂时也并没有用到,所以不做过多阐述。

用法:

<!--index.html-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>网站标题</title>
</head>
<body>
<!--导入自定义模板标签-->
{% load get_data %}
    <h1>我的第一个HTML页面</h1>
        <!--使用-->
    <p>我的年纪{{ "123456789"|truncate_chars}}</p>
</body>
</html>

这里只是做一个示例,你自己可以定义更强大的过滤器,毕竟我们缺少的只是想象力。

end

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容