文章目录
如何自定义Tag
代码位置
app 里 templatetags 包下面,记得 __init__.py
如:
polls/
__init__.py
models.py
templatetags/
__init__.py
poll_extras.py
views.py
模板里 {% load poll_extras %}
tags 是 模板里的占位符,表示一个函数,实现过滤格式转换等功能。
在项目中不是必须的,但是可以使得模板更加简洁,代码更优雅。一般位于 apps/templatetags/appname_tags.py
内置的tags和filters在:
django/template/defaultfilters.py
django/template/defaulttags.py
编写和调用 tags.py
- 导入模板装饰器
- 实例化注册器
register = template.Library()
- 用注册器装饰函数,制作 tag
from django import template
# 创建一个注册器
register = template.Library()
# 用注册器装饰函数,制作过滤器
@register.simple_tag
def datetimeformat(data):
""" 将 data 转成日期时间格式
"""
try:
return data.strftime(settings.DATE_TIME_FORMAT)
except Exception as e:
logger.error(e)
return ""
- 使用 tag
加载 {% load blog_tags %}
使用格式 {% tag data %}
例如:
{% datetimeformat article.pub_time %}
常见装饰器及其含义
- @register.simple_tag
- @register.filter(is_safe=True)
- @stringfilter
from django.template.defaultfilters import stringfilter
- @register.inclusion_tag(‘blog/tags/breadcrumb.html’)
写过滤器
使用 @register.filter 函数名即为过滤器名
is_safe == True django 会处理所有自动转义,表示这个过滤器是安全的,不会引入不安全的字符。
@stringfilter 是指定第一个参数只能是字符串
from django import template
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe
register = template.Library()
@register.filter(needs_autoescape=True)
def initial_letter_filter(text, autoescape=True):
first, other = text[0], text[1:]
# 如果需要自动转义则用 conditional_escape,
# 否则原样返回
if autoescape:
esc = conditional_escape
else:
esc = lambda x: x
result = '<strong>%s</strong>%s' % (esc(first), esc(other))
return mark_safe(result)
mark_safe 把结果标记为安全的,可以直接插入的那种,不用转义
@register.filter(expects_localtime=True)
说明第一个参数是本地时区时间
自定义 tags
simple_tag
@register.simple_tag 专门处理时间的
@register.simple_tag(takes_context=True) 需要用到 context
@register.simple_tag(takes_context=True)
def current_time(context, format_string):
timezone = context['timezone']
return your_get_current_time_method(timezone, format_string)
Inclusion tags
用法:{% show_results poll %}
模板:results.html
<ul>{% for choice in choices %}
<li> {{ choice }} </li>
{% endfor %}
</ul>
注册:
@register.inclusion_tag('results.html')
def show_results(poll):
choices = poll.choice_set.all()
return {'choices': choices}
返回:
<ul>
<li>First choice</li>
<li>Second choice</li>
<li>Third choice</li>
</ul>
如果没有对象直接使用上下文内容
设置: takes_context=True
@register.inclusion_tag('link.html', takes_context=True)
def jump_link(context):
return {
'link': context['home_link'],
'title': context['home_title'],
}
使用时候不用参数
{% jump_link %}
高级用法
模板工作流分两步:编译 compiling 渲染 rendering
编译:
把原始字符串切分成节点 nodes;
每个 节点都是 django.template.Node 的实例
都有 render() 方法
步骤:
0. 确定tag语法
<p>The time is {% current_time "%Y-%m-%d %I:%M %p" %}.</p>
- 编写编译函数
token.contents 指的是 {% %} 里面的 str 内容
split_contents() 会把 token 拆分成 2 到 3个部分
CurrentTimeNode 是你实现的Node类
from django import template
def do_current_time(parser, token):
try:
# split_contents() knows not to split quoted strings.
tag_name, format_string = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError(
"%r tag requires a single argument" % token.contents.split()[0]
)
if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):
raise template.TemplateSyntaxError(
"%r tag's argument should be in quotes" % tag_name
)
return CurrentTimeNode(format_string[1:-1])
- 渲染 render
继承 template.Node 实现 Node 类
def __init__(self, format_string)
提供参数
def render(self, context)
渲染参数并返回值
import datetime
from django import template
class CurrentTimeNode(template.Node):
def __init__(self, format_string):
self.format_string = format_string
def render(self, context):
return datetime.datetime.now().strftime(self.format_string)
- 注册tag
写法1:tag 名 和 函数名
register.tag(‘current_time’, do_current_time)
写法2:装饰器
@register.tag(name=“current_time”)
name 参数默认是函数名
把模板变量传递给tag
<p>This post was last updated at {% format_time blog_entry.date_updated "%Y-%m-%d %I:%M %p" %}.</p>
解析时带上参数
tag_name, date_to_be_formatted, format_string = token.split_contents()
传递给 Node
from django import template
def do_format_time(parser, token):
try:
# split_contents() knows not to split quoted strings.
tag_name, date_to_be_formatted, format_string = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError(
"%r tag requires exactly two arguments" % token.contents.split()[0]
)
if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):
raise template.TemplateSyntaxError(
"%r tag's argument should be in quotes" % tag_name
)
return FormatTimeNode(date_to_be_formatted, format_string[1:-1])
self.date_to_be_formatted = template.Variable(date_to_be_formatted)
模板变量时候使用 template.Variable
actual_date = self.date_to_be_formatted.resolve(context)
从上下文里反向解析出时间参数
class FormatTimeNode(template.Node):
def __init__(self, date_to_be_formatted, format_string):
self.date_to_be_formatted = template.Variable(date_to_be_formatted)
self.format_string = format_string
def render(self, context):
try:
actual_date = self.date_to_be_formatted.resolve(context)
return actual_date.strftime(self.format_string)
except template.VariableDoesNotExist:
return ''
解析到下一个block tag
{% upper %}This will appear in uppercase, {{ your_name }}.{% endupper %}
def do_upper(parser, token):
nodelist = parser.parse(('endupper',)) # 解析两者之间
parser.delete_first_token() # 删除 {% endupper %}`
return UpperNode(nodelist) # 传递给node
class UpperNode(template.Node):
def __init__(self, nodelist):
self.nodelist = nodelist
def render(self, context):
output = self.nodelist.render(context) #
return output.upper()