【Django】 03.Template

## 渲染模板: #### 两种方式: 1.`render_to_string`:找到模板,然后将模板编译后渲染成python的字符串格式。最后通过HttpResponse类包装成一个对象返回。
1
2
3
4
5
6
from django.http import HttpResponse
from django.template.loader import render_to_string

def index(request):
html = render_to_string('index.html')
return HttpResponse(html)
2.`render`:直接将模板渲染成字符串并包装成HttpResponse对象。
1
2
3
4
from django.shortcuts import render

def index(request):
return render(request, 'index.html')
## 模板路径配置: 1.模板文件应放在`template`文件夹中 2.调用模板文件,不用写文件夹路径,直接写名字就行,会默认从template文件夹调用 3.这部分设置在settings.py文件`TEMPLATE属性`中,可以更改其中的`DIRS`属性来更改默认查找位置 4.settings.py关于templates配置的属性介绍: - `DIRS`:这是一个列表,在这个列表中可以存放所有的模板路径,在使用render方法渲染函数时,会在这个列表中优先查找路径 - `APP_DIRS`:默认为`True`,设置为True时,会在`INSTALLED_APPS`的安装了的app下的templates文件夹中查找模板 - 查找顺序: - 先在DIRS列表下查找模板,如果有,就返回 - 如果DIRS列表中路径找不到,会检查当前视图函数所在的app是否安装,若安装,在当前这个app下的templates文件夹中查找模板 - 若当前app下templates文件夹下没找到模板,则在其他已安装的app的templates文件夹中查找 - 若还没找到,跑出TemplateDoesNotExist异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 要安装直接写进去
'front'
]

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'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',
],
},
},
]
## 模板变量: 1.模板中使用变量要在html文件中,把变量放在`{{ 变量 }}`中 2.变量内容放在render的`context`参数中,以`字典形式{'变量名': 变量值}`
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Person:
def __init__(self, username):
self.username = username

def index(request):

# 字符串型:
# content = {
# 'person': '鲁班大师'
# }

# 类属性型:
# p = Person('wxh')
# content = {
# 'person': p
# }

# 列表型:
# content = {
# 'person': [
# '鲁班一号',
# '程咬金',
# '李白',
# '刘禅'
# ]
# }

# 字典型:
content = {
'person': {
'user': 'wxh',
'admin': 'wxhnb'
}
}
return render(request, 'index.html', context=content)
3.四种变量访问方式: - 类属性:`变量名.属性名`
1
{{ person.uername }}
- 列表/元组:`变量名.下标`
1
{{ person.1 }}
- 字典:`变量名.key名`
1
{{ person.admin }} 
- 字符串:`变量名`
1
{{ person }}
4.注意:在变量类型是字典时,尽量不要使用`keys,values,items`等字典的方法来做key名,不然访问时会出现`{{ person.items }}`等会出现歧义 ## 模板标签: #### if语句标签:
1
2
3
4
5
6
7
{% if age >= 18 and age <= 100%}
<p>你是成年人了,请进</p>
{% elif age > 100 %}
<p>神仙给爷爬</p>
{% else %}
<p>未成年人滚蛋</p>
{% endif %}
1.所有标签都是在`{% 语句内容 %}`之间 2.if标签是闭合标签,就是要有结束标记`{% endif %}` 3.if标签内语法跟python中基本类似 #### for标签: `for...in...`类似python中的for...in...语句,可以遍历列表、元组、字符串、字典等可迭代对象。
1
2
3
4
5
<ul>
{% for book in books %}
<li>{{ book }}</li>
{% endfor %}
</ul>
如果想要反向遍历,在for..in..标签后加`reversed`
1
2
3
4
5
<ul>
{% for book in books reversed %}
<li>{{ book }}</li>
{% endfor %}
</ul>
`for..in..empty`:这个标签跟for..in...一样,只不过如果遍历的对象没有元素(即为空)时,会执行`empty`中的内容。
1
2
3
4
5
6
7
<ul>
{% for e in empty %}
<li>e</li>
{% empty %}
<li>暂时没有评论</li>
{% endfor %}
</ul>
遍历字典时,使用`items`,`keys`,`values`等方法
1
2
3
4
{% for key,value in goods.items %}
<li>{{ key }}</li>
<li>{{ value }}</li>
{% endfor %}
在遍历时,`DLT`提供了一些变量: - `forloop.counter`:当前循环的下标,从1开始 - `forloop.counter0`:当前循环的下标,从0开始 - `forloop.recounter`:当前循环的反向下标值,比如列表有5个元素,第一次遍历得到的值就是5。 - `forloop.first`:是否是第一次遍历 - `forloop.last`:是否是最后一次遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<tbody>
{% for person in persons %}
{% if forloop.first %}
<tr style="background: pink">
{% elif forloop.last %}
<tr style="background: aqua">
{% else %}
<tr>
{% endif %}
<td>{{ forloop.counter }}</td>
<td>{{ person.name }}</td>
<td>{{ person.age }}</td>
<td>{{ person.height }}</td>
</tr>
{% endfor %}
</tbody>
**注:模板中的for..in..没有`continue`和`break`语句** #### with标签: 1.用来定义便于调用 2.有两种使用方式 - with xxx=xxx - with xx as xx 3.定义变量仅在with标签内有效
1
2
3
4
5
6
7
8
9
10
11
12
{% with zs=person.0 %} 
<p>{{ zs }}</p>
<p>{{ zs }}</p>
<p>{{ zs }}</p>
{% endwith %}

<p>{{ zs }}</p> # 超出with语句,无效

{% with person.1 as ls %}
<p>{{ ls }}</p>
<p>{{ ls }}</p>
{% endwith %}
#### url标签: ``
1
2
3
4
# path部分,定义名字
path('book/', views.books, name='book')
# url标签部分
<a href="{% url 'book' %}">读书</a>
带参数url:
1
2
3
4
5
# path部分
path('book/detail/<book_id>/<category>/', views.book_detail, name='detail')

# url标签部分
<a href="{% url 'detail' book_id=1 category='好书' %}">图书id</a>
查询字符串url(拼接):
1
2
3
4
# path部分
path('login/', views.login, name='login')
# url标签部分
<a href="{% url 'login' %}?next=/">登陆</a>
#### autoscape自动转义标签: 1.DTL中默认已经开启了自动转义,会将特殊字符进行转义,如`<`转义为`<`等。 2.如果变量是确实可信任的,可以使用`autoscape`标签关掉自动转义。
1
2
3
4
5
6
7
8
9
10
11
content = {
'info': '<a href="#">百度</a>'
}

{% autoescape off %}
{{ info }}
{% endautoescape %}
# 转义后结果一个百度超链接
# 与<a href="#">百度</a>等价

# 若不关闭自动转义,即直接使用{{ info }},会输出<a href="#">百度</a>而不是可点击的超链接
#### spaceless标签: 作用:去掉作用域内的代码空白
1
2
3
4
5
6
7
{% spaceless %}
<p>
<a href="#">谷歌</a>
</p>
{% endspaceless %}

# 等价与<p><a href="#">谷歌</a></p>
#### verbatim标签: 作用:去除DTL对作用域内代码的解析,如{{ xxx }}会被解析为变量
1
2
3
4
5
6
7
8
9
content = {
'info': 'hello'
}
# 输出为{{ info }},防止被解析成变量
{% verbatim %}
{{ info }}
{% endverbatim %}

# 不使用verbatim标签的话会输出hello
## 过滤器: #### add过滤器: {{ value|add:arg }} 如果两个参数都是: - 整形或者是数字字符串,进行数值运算 - 字符串型,进行拼接 - 列表,进行组合
1
2
3
4
5
6
7
8
9
10
# 源码
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 ''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
content = {
'value1': [1, 2, 3],
'value2': [7, 8, 8],
'str1': 'abc',
'str2': 'jkl',
'int_str1': '1',
'int_str2': '2',
'int1': 100,
'int2': 200
}

{{ value1|add:value2 }} --> [1, 2, 3, 7, 8, 8]
{{ str1|add:str2 }} --> abcjkl
{{ int_str1|add:int_str2 }} --> 3
{{ int1|add:int2 }} --> 300
#### cut过滤器: {{ value|cut:arg }} 移除所有value中arg所指定的字符串。类似python中的`replace(str,'')`
1
2
3
4
5
6
7
 content = {
'word': 'hello world'
}

{{ word|cut:'he' }}
# 输出为 llo world

#### data过滤器: {{ value|date:arg }} value传入日期,arg传入格式化方法代码 将日期按照指定格式格式化为字符串。
1
2
3
4
5
6
content = {
'time': datetime.now()
}

{{ time|date:'Y/m/d h:i:s' }} -->当前时间输出为 年/月/日 时:分:秒格式
{{ time|date:'d-n-Y H:i:s' }} -->当前时间输出为 日-月-年 时:分:秒格式
常用格式字符 | 描述 | 示例 ---|---|--- Y | 四位年份 | 2019 m | 两位数字的月份 | 01-12 n | 月份,1-9前没有0 | 1-12 d | 两位数字的天 | x-09 j | 天,1-9前面没有0 | x-9 g | 小时,12小时格式,1-9前没有0 | 9:xx:xx G | 小时,24小时格式,1-9前没有0 | 21:xx:xx h | 小时,12小时格式,1-9前有0 | 09:xx:xx H | 小时,24小时格式,1-9前有0 | 21:xx:xx i | 分钟,1-9前有0 | xx:01:xx s | 秒,1-9前有0 | xx:xx:05 #### default过滤器: {{ value|default:arg}} 将`False`值,比如`[]`,`''`,`None`,`{}`等在if判断中判定为False的值,都会使用False过滤器提供的默认值。 {{ value|default:arg }} 用arg值替换为False的value值,不为False则不处理,返回原value值。
1
2
3
4
5
6
7
8
9
10
context = {
'value1': '',
'value2': [],
'value3': {},
'value4': None
}
{{ value1|default:'我是""默认值' }}
{{ value2|default:'我是[]]默认值' }}
{{ value3|default:'我是{}默认值' }}
{{ value4|default:'我是None默认值' }}
#### default_if_none过滤器: {{ value|default_if_none:arg }} 功能同default,不过只有在值为none时才过滤,其他false值不过滤。
1
2
3
4
5
6
7
8
context = {
'value1': '',
'value2': [],
'value3': {},
'value4': None
}
{{ value2|default_if_none:'我是[]默认值' }} # 输出为 [],不会过滤
{{ value4|default_if_none:'我是None默认值' }} # 输出为 我是None默认值
#### first,last过滤器: {{ value|first }} {{ value|last }} 取出列表中第一/最后一个元素。
1
2
3
4
5
 context = {
'value': [1, 2, 3]
}
{{ value|first }} # 1
{{ value|last }} # 3
#### floatformat过滤器: {{ value\|floatformat[:arg] }} 四舍五入的方式格式化浮点型数据。如果没有传参数,会默认保留1位小数,若小数后面全为0,则只保留整数。可以传递参数来标识具体保留几位小数。 1. 没传参数: value | 模板代码 | 输出 ---|---|--- 34.23234 | {{ value\|floatformat }} | 34.2 34.0000 | {{ value\|floatformat }} | 34 34.260 | {{ value\|floatformat }} | 34.3 2. 传参数; value | 模板代码 | 输出 ---|---|--- 34.23234 | {{ value\|floatformat:3 }} | 34.232 34.00000 | {{ value\|floatformat:3 }} | 34.000 34.26000 | {{ value\|floatformat:3 }} | 34.260 #### join过滤器: {{ value|join:arg }} 相当于python中的join方法,用一个字符串当连接符,连接一个列表。
1
2
3
4
5
6
7
context = {
'value': ['w', 'a', 'z', 'd']
}

{{ value|join:'==' }}

# 输出为 w==a==z==d
#### lower upper过滤器: {{ value|lower }} {{ value|upper }} 大小写转换
1
2
3
4
5
6
context = {
'value_low': 'hello',
'value_up': 'HELLO',
}
{{ value_up|lower }} # --> hello
{{ value_low|upper }} # --> HELLO
#### random过滤器: {{ value|random }} 作用相当于python中的random.choice,从可迭代对象中随机取出一个值。
1
2
3
4
5
context = {
'value': list(range(32))
}

{{ value|random }}
#### safe过滤器: {{ value|safe }} 相当于autoscape自动转义标签,防止value值被转义
1
2
3
4
5
context = {
'value': '<div style="background: aqua">safe成功</div>'
}

{{ value|safe }}
#### slice过滤器: {{ value|slice:arg }} 切片操作,arg参数写法同列表切片操作
1
2
3
4
5
6
7
8
context = {
'value': list(range(32))
}

{{ value|slice:'10:' }}
{{ value|slice:':15' }}
{{ value|slice:'3:10' }}
{{ value|slice:':10:2' }} # 取步长
#### striptags过滤器; {{ value|striptags }} 去除value中的html标签
1
2
3
4
5
context = {
'value': '<p>是我dio哒</p>'
}
{{ value }} # --><p>是我dio哒</p>
{{ value|striptags }} # -->是我dio哒
#### truncatechars过滤器: {{ value|truncatechars:arg }} 如果给定的value字符串超过了所给定的长度,会进行切割,并拼接上`...`. - 如果字符串长度小于给定的arg,不会进行操作 - 给定的arg包含了`...`的长度,所以若保留两个字符,应给定arg=5
1
2
3
4
5
6
7
context = {
'value': '北京欢迎你~',
'value1': '<p>北京欢迎你~</p>'
}

{{ value|truncatechars:5 }} # -->北京...

#### truncatechars_html过滤器: {{ value1|truncatechars_html:arg }} 作用同truncatechars,不过在切割时会保留html标签。
1
2
3
4
5
6
7
context = {
'value': '北京欢迎你~',
'value1': '<p>北京欢迎你~</p>'
}

{{ value1|truncatechars:5 }} # <p...
{{ value1|truncatechars_html:5 }} # <p>北京...</p>
## 自定义过滤器: 1. 在app下创建一个python包,叫`templatetags`。注意,必须叫这个名字,不然django找不到。 2. 在这个包下面创建一个py文件,用来存储过滤器。 3. 在新建的py文件中,定义过滤器(也就是函数)。函数第一个参数是被过滤的值也就是value,第二个参数是arg,可有可无。最多两个参数。 4. 写完过滤器后,要注册过滤器。
1
2
3
4
5
from django import template

register = template.Library()

register.filter(name, filter_func)
5. 把这个过滤器所在的app添加到`settings.py`文件中的`INSTALLED_APP`中,不然找不到过滤器。 6. 在html模板中使用load标签加载过滤器所在的py文件。
1
{% load py文件名 %}
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# my_filter.py文件中的代码

from django import template


def greet(value, word):
return value + word


# 创建一个注册对象
register = template.Library()
# 使用对象注册自定义的过滤器
register.filter('greet', greet) # 第一个参数写调用时的名称,第二个参数是过滤器对应的函数

# 或者使用装饰器注册,会直接用函数名当成调用名注册
# register = template.Library()
# @register.filter
# def greet(value, word):
# return value + word

# 若想自定义调用名,可在装饰器传参
# @register.filter('my_greet')
# def greet(value, word):
# return value + word

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- html模板文件的代码-->

{% load my_filter %} <!-- 这句话用来加载自定义的过滤器文件-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ value|greet:'还钱' }} <!-- 调用自定义的过滤器-->
</body>
</html>
1
2
3
4
5
# 过滤器使用
context = {
'value': '张三'
}
{{ value|greet:'还钱' }} # --> 张三还钱
## 模板结构优化: #### include标签: 提高模板代码复用性。有些模板的代码是重复的,可以单独抽取出来,以后哪里用到使用`include`导入进来。 如果想在`include`子模板时传递一些参数,可以使用`with xxx=xxx`的形式。
1
2
3
4
5
6
7
8
9
10
11
# include的代码
<header>
<ul>
<li><a href="{% url 'index' %}">首页</a></li>
<li><a href="{% url 'school' %}">学校</a></li>
<li><a href="{% url 'company' %}">公司</a></li>
<li>{{ username }}</li> {# 带参数 #}
</ul>
</header>

{% include 'header.html' with username='wxh' %}
#### 继承: 子模板可以继承父模板的html代码,如果子模板需要有自己的代码,通过block接口实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--父模板代码base.html-->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li><a href="{% url 'index' %}">首页</a></li>
<li><a href="{% url 'school' %}">学校</a></li>
</ul>
<!--block用来给子模板提供自定义代码接口-->
{% block content %} <!--content是定义block名,调用时要有名字-->
<!--父模板block内的文字子模板如果需要调用要是用{{ block.super }}变量调用-->
这是父模板的文字
{% endblock %}
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
<!--子模板调用代码-->

{% extends 'base.html' %} <!--这句话表示该模板继承自哪个模板,要写在代码开头-->

{% block content %}
<p>这是子模板的文字</p>
{{ block.super }} <!--调用父模板block中的文字-->
{% endblock %}

<!--外面的代码不会输出,无效代码-->
这是block外面的代码

## 加载静态文件: 使用static标签 1. 确保`INSTALLED_APP`中添加了`django.contrib.staticfiles`这条。(默认添加) 2. 确保在settings.py中设置了`STATIC_URL`。(默认设置已经为`STATIC_URL = '/static/'`即可) 3. 创建文件夹存储静态文件。 - 在app下创建static文件夹以存储app所使用的静态文件。为了防止app间的静态文件存在同名不能分辨的现象,推荐在app下的static文件夹下新建一个与app同名的文件夹,将静态文件放入其中。这样调用时路径就为`app_name/xxx.jpg`同名文件不会冲突。 - 一些静态文件与app不挂钩。可以在settings.py文件中添加`STATICFILES_DIRS`属性,以后DTL就会在这个属性所指向的路径下查找静态文件。
1
2
3
4
# 这里路径设置为项目根路径下的static文件夹
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
5. 在模板中使用`load`标签加载static标签。因为static不是DTL的内置标签,若要想省去这步,可在settings.py文件的TEMPLATES下的OPTIONS下添加属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'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',
],
####添加这条
'builtins': ['django.templatetags.static'], # 添加这句话来使static成为内置标签
####添加这条
},
},
]
6. 使用`static`标签导入静态文件。
1
2
<img src="{% static 'front/gologo.jpeg'%}" alt=""> # 导入图片
<link rel="stylesheet" href="{% static 'index.css' %}"> # 导入css样式

【Django】 03.Template
http://blog.rainna.xyz/2020/11/22/2020-11-22-03.Template/
作者
rainnalv
发布于
2020年11月22日
许可协议