Django 笔记之 Tags 自定义标签

如何自定义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

  1. 导入模板装饰器
  2. 实例化注册器 register = template.Library()
  3. 用注册器装饰函数,制作 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 ""
  1. 使用 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>

  1. 编写编译函数
    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])
  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)
  1. 注册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()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值