20190325 Django自定义过滤器和自定义模板标签
代码布局(死去活来)
自定义要先解决几个问题:
1). 在哪里定义,要将代码写在哪里?
- app目录下的名为templatetags的文件夹,必须叫这个名字。这个文件夹必须是一个python的包(python package),这个文件夹下一定有一个
__init__.py
的文件 - 自定义的模板标签,或模板过滤器就放在这个包下的python模块中(python脚本文件)
- app必须在settings中被注册
I.自定义模板过滤器
定义
-
自定义过滤器就是一个python函数,它有1个或2个参数。
- 第一个参数就是那个传进来的模板变量
- 第二个参数是一个普通参数,可以是默认参数,也可以不要这个参数
-
依旧以teacher应用的案例为例子:如何将性别从男1女0的二进制显示变成我们看得懂的汉字或者英文字男male女female呢?简言之,就是接收0和1,返回女和男
注册并定义一个过滤器:
from django.template import Library
register = Library() # 必须用register这个变量名
def my_male(value):
'''
转换性别的过滤器
:param value:
:return:
'''
map = {
0:'女',
1:'男'
}
return map[value]
register.filter(my_male) # 注册自定义过滤器
在模板中使用
-
Step1. load标签,引入自定义的过滤器模块,也就是templatetags包里的那个python文件teacher_tags.py
{% load teacher_filters %}
......
-
step2. 在表格tbody当中,写入在过滤器文件中定义好的过滤器名my_male,
<td>{{ student.sex|my_male }}</td>
注意:新建了templatetags的文件夹,要重启一下django服务。
2个参数:
需求:有中英文双语显示性别,则可以传入两个参数:
'''
转换性别的过滤器
:param value:
:return:
'''
map = {
'zh': {0:'女', 1:'男'},
'en': {0:'Female', 1:'Male'},
}
return map[value][lang]
给变量命名
过滤器变量的名字可以根据实际需要自己定义,避免名字重复或者名字太长的情况。
比如把def my_male(value, lang):
改成 def aaa(value, lang)
然后在模板html里面将原来的<td>{{ student.sex|my_male }}</td>
改成<td>{{ student.sex|aaa }}</td>
II.自定义模板标签
干任何事情
标签类型
1. 简单标签
django.template.Library.simple_tag()
- 接受许多参数,并根据输入的参数和外部信息,进行处理后返回结果
创建、注册简单标签
代码布局在templatetags目录下,新建一个teacher_tags.py的文件
from django.template import Library
from datetime import datetime
register = Library() # 必须用register这个变量名,必须要写
def current_time(format_str='%Y-%m-%d %H:%M:%S'):
"""就输出当前时间"""
return datetime.now().strftime(format_str)
#注册
register.simple_tag(current_time, name='current')
在模板中使用
{% load teacher_tags %}
.....
<h1>学生列表</h1>
<p>当前时间:{% current %}</p>
<插入定义好的过滤器加载到模板当中图片>
运行结果:
传值
{% current format_str= %}
运行结果:
另外,{% current '%Y-%m-%d' %}
也一样能显示出年月日格式的当前时间
模板标签就是一个函数,跟普通函数的传参一样,可以传字符串、模板变量、关键字参数
再举个例子:
先在teacher应用目录下的views视图文件里,我们将上下文context函数中传入’format_str’:’%H:%M:%S’,
return render(request, 'teacher/student_list_page.html',
context={
'students':students,
'format_str': '%H:%M:%S',
})
再回到学生页面模板(也就是前端html页面)中,此时format_str则作为一个模板变量。
<p>当前时间:{% current format_str %}</p>
运行结果:
使用上下文变量
在自定义标签中,使用当前模板中的所有的上下文变量context
def current_time(context, format_str='%Y-%m-%d %H:%M:%S'):
return datetime.now().strftime(format_str)
register.simple_tag(current_time, name='current',takes_context=True)
语法格式:context``````take_context
组成一对
class=‘new_year’
III. 包含标签(装饰器)
定义
通过渲染另外一个模板展示数据,以后django admin就会用到这个
@register.inclusion_tag('teacher/show_list.html')
def show_list(list_data):
# 一个嵌套标签,实现展示列表数据
return {'ls': list_data}
继续以teacher应用中学生列表的例子为范例:
先在views里面的student_list_view和student_detail_view里面的学生列表中加入hobby变量,并传入参数列表
def student_list_view(request):
students = [
{'id': 1, 'name': 'Adam', 'age': 19, 'sex': 1, 'hobby': ['篮球', '足球', '排球']},
{'id': 3, 'name': 'Betty', 'age': 19, 'sex': 0, 'hobby': ['羽毛球', '毽子', '乒乓球']},
{'id': 11, 'name': 'Cathy', 'age': 18, 'sex': 0, 'hobby': ['羽毛球', '毽子', '乒乓球']},
{'id': 12, 'name': 'Daisy', 'age': 20, 'sex': 0, 'hobby': ['羽毛球', '毽子', '乒乓球']},
{'id': 15, 'name': 'Ethan', 'age': 19, 'sex': 1, 'hobby': ['篮球', '足球', '排球']},
{'id': 26, 'name': 'Adda', 'age': 19, 'sex': 0, 'hobby': ['篮球', '网球', '排球']},
{'id': 27, 'name': 'Bryan', 'age': 20, 'sex': 1, 'hobby': ['篮球', '足球', '排球']},
{'id': 38, 'name': 'Carmen', 'age': 18, 'sex': 0, 'hobby': ['篮球', '网球', '排球']},
{'id': 39, 'name': 'Dolphin', 'age': 20, 'sex': 0, 'hobby': ['篮球', '网球', '排球']},
{'id': 40, 'name': 'Evan', 'age': 19, 'sex': 1, 'hobby': ['篮球', '网球', '排球']},
]
def student_detail_view(request, pk):
students = {
1: {'id': 1, 'name': 'Adam', 'age': 19, 'sex': 1, 'hobby':['篮球','足球','排球']},
3: {'id': 3, 'name': 'Betty', 'age': 19, 'sex': 0, 'hobby':['羽毛球','毽子','乒乓球']},
11: {'id': 11, 'name': 'Cathy', 'age': 18, 'sex': 0, 'hobby':['羽毛球','毽子','乒乓球']},
12: {'id': 12, 'name': 'Daisy', 'age': 20, 'sex': 0, 'hobby':['羽毛球','毽子','乒乓球']},
15: {'id': 15, 'name': 'Ethan', 'age': 19, 'sex': 1, 'hobby':['篮球','足球','排球']},
26: {'id': 26, 'name': 'Adda', 'age': 19, 'sex': 0, 'hobby':['篮球','网球','排球']},
27: {'id': 27, 'name': 'Bryan', 'age': 20, 'sex': 1, 'hobby':['篮球','足球','排球']},
38: {'id': 38, 'name': 'Carmen', 'age': 18, 'sex': 0, 'hobby':['篮球','网球','排球']},
39: {'id': 39, 'name': 'Dolphin', 'age': 20, 'sex': 0, 'hobby':['篮球','网球','排球']},
40: {'id': 40, 'name': 'Evan', 'age': 19, 'sex': 1, 'hobby':['篮球','网球','排球']},
}
再回到学生列表模板前端html文件里添加一个表头"爱好",再在单元格td标签中添加ul标签,其中插入for标签,写入for item in student.hobby调用item,并加入带li标签的模板变量item
<table class="table">
<thread>
<tr>
<th>学号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>爱好</th>
</tr>
</thread>
<tbody>
{% for student in students %}
<tr {% if student.sex == 0 %} style="background-color: lightpink"{% else %} style="background-color: aqua" {% endif %}>"
<td><a href="{% url 'teacher:student_detail' student.id %}">{{ student.id }}</a></td>
<td>{{ student.name }}</td>
<td>{{ student.age }}</td>
<td>{{ student.sex|my_male:'en' }}</td>
<td>
<ul>
{% for item in student.hobby %}
<li>{{ item }}</li>
{% endfor %}
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
思考:当我们的模板里有很多数据需要填进去,并且还要设置不同的样式(CSS),有没有其他办法可以让这些数据填入在渲染的时候不用那么麻烦呢?那我们就可以用到包含标签(装饰器)的方法了。
在teacher_tags页面,新增定义
def show_list(list_data):
'''
这是一个嵌套标签,实现展示列表数据的功能
:param list_data:
:return:
'''
return {'ls': list_data}
并在templates目录的teacher目录下新建一个show_list.html的模板前端页面。
在teacher_tags标签页顶部插入以下指令导入get_template模块
from django.template.loader import get_template
在return下方加入以下指令来导入该新建的模板:
t = get_template('teacher/show_list.html')
register.inclusion_tag(show_list)
这里我们可以用一个装饰器来实现同样的功能:
@register.inclusion_tag('teacher/show_list.html')
def show_list(list_data):
'''
这是一个嵌套标签,实现展示列表数据的功能
:param list_data:
:return:
'''
return {'ls': list_data}
这样就可以把t = get_template('teacher/show_list.html')
register.inclusion_tag(show_list)
去掉了。
在新建的show_list.html里写入:
<ul>
{% for l in ls %}
<li>{{ l }}</li>
{% endfor %}
</ul>
那么学生列表页student_list_page前端里关于hobby的td标签就可以缩减为{% show_list student.hobby %}
<table class="table">
<thread>
<tr>
<th>学号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>爱好</th>
</tr>
</thread>
<tbody>
{% for student in students %}
<tr {% if student.sex == 0 %} style="background-color: lightpink"{% else %} style="background-color: aqua" {% endif %}>"
<td><a href="{% url 'teacher:student_detail' student.id %}">{{ student.id }}</a></td>
<td>{{ student.name }}</td>
<td>{{ student.age }}</td>
<td>{{ student.sex|my_male:'en' }}</td>
<td>
{% show_list student.hobby %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
同样,在学生详情页的前端页面,也可以通过包含标签的方法,实现简洁的代码编写。
这个show_list.html等于单独拿出来做了一个格式模板,只负责显示格式的功能渲染,这样,当表格的数据项目有很多时,你就不需要去为每一个数据项目再去设置格式那么繁琐了。
引用上下文变量
@register.inclusion_tag('teacher/show_list.html', takes_context=True)
def show_list(context, list_data):
'''
这是一个嵌套标签,实现展示列表数据的功能
:param list_data:
:return:
'''
return {'ls': list_data, 'context': context}