DTL为Django 内置的模板语言,可以实现和Django的无缝衔接。
render_to_string
找到模板,然后把模板编译后渲染成Python的字符串格式,最后再通过HttpResponse 类包装成一个HttpResponse对象返回回去。
from django.template.loader import render_to_string
from django.http import HttpResponse
def book_detail(request,id):
html = render_to_string("index.html")
return HttpResponse(html)
django给提供了一个更为简便的方式,直接将模板渲染成字符串和包装成HttpResponse对象。
from django.shortcuts import render
def book_list(request):
return render(request,'list.html')
在setting.py文件中,有一个TEMPLATES配置,这个配置包含了模板引擎的配置。
DIRS
这是一个列表,在这个列表中可以存放所有的模板路径,以后在视图中使用render或者render_to_string 渲染模板时,会在这个列表中查找模板。
APP_DIRS
默认true,会在INSTEALLED_APPS的安装了的APP下的templates文件中查找模板。
查找顺序
先在DIRS这个列表中依次查找路径下有没有模板;如果没有,再检查当前视图所处的app是否已经安装了,如果已经安装了,那么就在当前app下的templates文件夹中查找模板;如果没有再去其他已经安装的app中查找。如果没有抛出异常,TemplateDoesNotExist。
变量
模板中可以包含变量。在视图中通过render或render_to_string的context参数,为一个字典类型。
# index.html 模板
<p>{
{
username }}</p>
# views.py 视图
def index(requets):
return render(request,'index.html',context={
'username':'zzz'}
模板中的变量也支持(.) 比如 person.username
模板标签
if 标签
if 标签相当于 Python 中的 if 语句,有 elif 和 else 相对应,但是所有的标签都需要用标签符号( {%%} )进行包裹。 if 标签中可以使用 ==、!=、<、<=、>、>=、in、not in、is、is not 等判断运算符
for…in … 标签
for…in… 标签: for…in… 类似于 Python 中的 for…in… 。可以遍历列表、元组、字符串、字典等一切可以遍历的对象。
如果想要反向遍历,那么在遍历的时候就加上一个 reversed 。
for…in…empty 标签
这个标签使用跟 for…in… 是一样的,只不过是在遍历的对象如果没有元素的情况下,会执行 empty 中的内容。
{
% for person in persons %}
<li>{
{
person }}</li>
{
% empty %}
暂时还没有任何人
{
% endfor %}
with 标签
在模版中定义变量。有时候一个变量访问的时候比较复杂,那么可以先把这个复杂的变量缓存到一个变量上,以后就可以直接使用这个变量就可以了。
context = {
"persons": ["张三","李四"]
}
{
% with lisi=persons.1 %}
<p>{
{
lisi }}</p>
{
% endwith %}
{
% with persons.1 as lisi %}
<p>{
{
lisi }}</p>
{
% endwith %}
注意with中定义的变量 只能在with中使用;定义变量时,等号两天不能留有空格。
url 标签
在模板中,可以使用反转的方式来实现url。
# path部分
path('detail//' ,views.book_detail,name='detail')
<a href="{% url 'book:detail' %}">图书列表页面</a>
# url反转,使用位置参数
<a href="{% url 'book:detail' 1 %}">图书详情页面</a>
# url反转,使用关键字参数
<a href="{% url 'book:detail' book_id=1 %}">图书详情页面</a>
# 要传递查询字符串的参数
<a href="{% url 'book:detail' book_id=1 page=2 %}">图书详情页面</a>
spaceless 标签
移除html标签中的空白字符
{
% spaceless %}
<p>
<a href="foo/">Foo</a>
</p>
{
% endspaceless %}
autoescape 标签
开启和关闭这个标签内元素的自动转义功能。
# 传递的上下文信息
context = {
"info":"百度"
}
# 模板中关闭自动转义
{
% autoescape off %}
{
{
info }}
{
% endautoescape %}
verbatim 标签
默认在DTL模板中会解析那些特殊字符。如果不想使用DTL的解析引擎,可以把代码放在verbatim中。
模板常用过滤器
add
将传进来的参数添加到原来的值上面。如果可以转换成整形,那就相加;如果不能就进行拼接。{ { value|add:"2" }}
如果value为4,则结果为6。
# add过滤器源码
def add(value, arg):
"""Add the arg to the value."""
try:
return int(value) + int(arg)
except (ValueError, TypeError):
try:
return value + arg
except Exception:
return ''
cut
移除值中所有指定的字符串。类似于python中的replace(args,"")。{ { value|cut:" " }}
def cut(value, arg):
"""Remove all values of arg from the given string."""
safe = isinstance(value, SafeData)
value = value.replace(arg, '')
if safe and arg != ';':
return mark_safe(value)
return value
date
将一个日期按照指定的格式,格式化成字符串。
# 数据
context = {
"birthday": datetime.now()
}
# 模版
{
{
birthday|date:"Y/m/d" }} # 输出 0000/00/00
Y 四位数字的年份 2018
m 两位数字的月份 01-12
n 月份,1-9前面没有0前缀 1-12
d 两位数字的天 01-31
j 天,但是1-9前面没有0前缀 1-31
g 小时,12小时格式的,1-9前面没有0前缀 1-12
h 小时,12小时格式的,1-9前面有0前缀 01-12
G 小时,24小时格式的,1-9前面没有0前缀 1-23
H 小时,24小时格式的,1-9前面有0前缀 01-23
i 分钟,1-9前面有0前缀 00-59
s 秒,1-9前面有0前缀 00-59
default
如果值被评估为False,都会使用default过滤器提供的默认值。{ { value|default:"nothing" }}
如果 value 是等于一个空的字符串。比如 “” ,那么以上代码将会输出 nothing 。
default_if_none
如果值是 None ,那么将会使用 default_if_none 提供的默认值。
first
返回列表/元组/字符串中的第一个元素。{ { value|first }}
last
返回列表/元组/字符串中的最后一个元素。
floatformat
使用四舍五入的方式格式化一个浮点类型。
join
将列表/元组/字符串。{ { value|join:"/" }}
length
获得一个列表/元组/字符串/字典的长度。{ { value|length }}
lower
将值中所有的字符全部转换成小写。{ { value|lower }}
upper
是将指定的字符串全部转换成大写。
random
在被给的列表/字符串/元组中随机的选择一个值。{ { value|random }}
safe
标记一个字符串是安全的。也即会关掉这个字符串的自动转义。
slice
类似于 Python 中的切片操作。{ { some_list|slice:"2:" }}
stringtags
删除字符串中所有的 html 标签。
truncatechars
如果给定的字符串长度超过了过滤器指定的长度。那么就会进行切割,并且会拼接三个点来作为省略号。
truncatechars_html
不会切割 html 标签 { { value|truncatechars:5 }}
自定义模板过滤器
模版过滤器必须要放在 app 中,并且这个 app 必须要在 INSTALLED_APPS 中进行安装。然后再在 这个 app 下面创建一个 Python包 叫做 templatetags 。再在这个包下面创建一个 python文件 。
过滤器实际上就是python中 的一个函数,只不过是把这个函数注册到模板库中,以后在模板中就可以使用这个函数了。但是这 个函数的参数有限制,第一个参数必须是这个过滤器需要处理的值,第二个参数可有可无,如果 有,那么就意味着在模板中可以传递参数。并且过滤器的函数最多只能有两个参数。在写完过滤 器后,再使用 django.template.Library 对象注册进去。
from django import template
# 创建模板库对象
register = template.Library()
#过滤器函数 value 需要处理的值 mystr 可以传递的参数
def mycut(value,mystr):
return value.replace(mystr)
#将函数注册到模板库中
register.filter("mycut",mycut)
以后想要在模板中使用这个过滤器,就要在模板中load一下这个过滤器所在的模块的名字。
{
% load my_filter %}
# 自定义时间计算过滤器
# time_filter.py 文件
from datetime import datetime
from django import template
register = template.Library()
def time_since(value):
"""
time距离现在的时间间隔
1.如果时间间隔小于1分钟,那么就显示"刚刚"
2.如果是大于1分钟小于1小时,那么就显示“xx分钟前”
3.如果是大于1小时小于24小时,那么就显示“xx小时前”
4.如果是大于24小时小于30天以内,那么就显示“xx天前”
5.否则就是显示具体的时间 2017/10/20 16:15
"""
if isinstance(value,datetime):
now = datetime.now()
timestamp = (now - value).total_seconds()
if timestamp < 60:
return "刚刚"
elif timestamp >= 60 and timestamp < 60*60:
minutes = int(timestamp/60)
return "%s分钟前" % minutes
elif timestamp >= 60*60 and timestamp <60*60*24:
hours = int(timestamp/(60*60))
return "%s小时前" % hours
elif timestamp >= 60*60*24 and timestamp < 60*60*24*30:
day = int(imestamp / (60*60*24))
return "%s天前" % days
else:
return value.strftime("%Y/%m/%d %H:%M")
else:
return value
register.filter("time_since",time_since)
在模板中使用的示例
{
% load time_filter %}
...
{
% value|time_since %}
...
为了更加方便的将函数注册到模板库中,当作过滤器,也可以使用装饰器来将一个函数包装成过滤器。
from django import template
register = template,Library()
@register.filter(name='mycut')
def mycut(value,mystr):
return value.replace(mystr,"")
模块结构优化
引用模块
有时候一些代码是在许多模板中都用到的。如果我们每次都重复去拷贝代码,那肯定不符合项目的规范。一般我们可以把这些重复性的代码抽取出来,以后想要使用这些代码的时候,通过include包含进来。
# header.html
<p>heaer</p>
# footer.html
<p>footer</p>
#main.html
{
% include 'header.html' %}
<p>main</p>
{
% include 'footer.html' %}
可以用过with语句来传递参数
# header.html
<p>用户名 {
{
username }}</p>
# main.html
{
% include "header.html" with username='huangyong' %}
模板继承
模版继承也可以在父模版中先定义好一些 子模版需要用到的代码,然后子模版直接继承就可以了。并且因为子模版肯定有自己的不同代码, 因此可以在父模版中定义一个block接口,然后子模版再去实现。
# base.html
{
% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="{% static 'style.css' %}" />
<title>{
% block title %}我的站点{
% endblock %}</title>
</head>
<body>
<div id="sidebar">
{
% block sidebar %}
<ul>
<li><a href="/">首页</a></li>
<li><a href="/blog/">博客</a></li>
</ul>
{
% endblock %}
</div>
<div id="content">
{
% block content %}{
% endblock %}
</div>
</body>
</html>
# 子模板 通过extends标签来实现
{
% extends "base.html" %} # 注意必须放在模板的第一行
{
% block title %}博客列表{
% endblock %} # 子模板必须放在block中
{
% block content %}
{
% for entry in blog_entries %}
<h2>{
{
entry.title }}</h2>
<p>{
{
entry.body }}</p>
{
% endfor %}
{
% endblock %} # ,还可以在 block 结束的时候也定义上名字}{% endblock content %}
加载静态文件
使用static标签来加载静态文件。
首先确保django.contrib.staticfiles 已经添加到setting.INSTALLED_APPS中
确保在settings.py中设置了STATIC_URL。
在已经安装了的app下创建一个文件夹叫做static,然后在这个static文件夹中创建一个当前app的名字的文件夹,再把静态文件放在这个文件夹下。比如 book/static/book/logo.jpg
如果存在一些静态文件和任何app无关的,那么可以再setting.py中添加STATICFILES_DIRS
,然后在根目录下创建static
STATICFILES_DIRS = [
os.path.join(BASE_DIR,"static")
]
在模板中使用load标签加载static标签。
{
% load static %}
<link rel="stylesheet" href="{% static 'style.css' %}">
如果不想每次在模版中加载静态文件都使用 load 加载 static 标签,那么可以 在 settings.py 中的 TEMPLATES/OPTIONS 添加 ‘builtins’: [‘django.templatetags.static’] ,这样以后在模版中就可以直接使用 static 标签,而不用 手动的 load 了。