zoukankan      html  css  js  c++  java
  • 定制Django的Tag和Filter(二)

    配置

    (1)最常见的放置自定义Tag和Filter的地方是在Django的app下。当一个app被添加到settings.py的INSTALLED_APPS 后,任何在它下面的合法位置将自动的可在templates中被调用。合法位置就是在app下的templatetags子文件夹下,但不要忘记添加init.py文件来表明它是个包。

    自定义的tag或者filter就在templatetag文件夹下的py文件中,在template中调用的是该py文件的名字,比如

    app名字是polls,tags/filter在poll_extras.py中:

    polls/
        __init__.py
        models.py
        templatetags/
            __init__.py
            poll_extras.py
        views.py
    

    在template中这样加载定义在poll_extras.py中的tags/filters:

    {% load poll_extras %}
    

    包含定制的tags/filters的app必须在INSTALLED_APPS,并且{% load ... %}也是在INSTALLED_APPS从上往下搜索。

    (2)当然放置Tag和Filter的文件也不一定非得在app中,可以在project的任何地方,但是这种情况下,需要在DjangoTemplateslibraries中注册,比如,将放置tag/filters的文件testcommon.py放置在工程的新建common文件夹下。

    然后在settings.py的TEMPLATE中注册该文件,需要注意的是在templates中load的是‘commonfile'。

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [TEMPLATE_DIR],
            'APP_DIRS': True,
            'OPTIONS': {
                # 'string_if_invalid':InvalidTemplateVariable('%s'),
                # 'autoescape':False,
                'context_processors': [
                    
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                    'banners.processors.onsale',
                    
                ],
                'libraries':{'commonfile':'testBegin.common.testcommon'},
            },
        },
    ]
    

    testcommon.py中定义commonfilter:

    from django import template
    from django.utils.html import escape
    from django.utils.safestring import mark_safe
    register=template.Library()
    @register.filter(needs_autoescape=True)
    def commonfilter(value,autoescape=True):
        if autoescape:
            value=escape(value)
        result='<b>%s</b>'%value 
        return mark_safe(result)
    

    最后在template中调用:

    {% load commonfile %}
    {{ coffee|commonfilter }}  #其中coffee变量为'mocamoca'
    

    那能不能像built-in filter一样,不用每次都声明{% load custom filter %}?可以的,只需要在settings.py中的'TEMPLATES'的'OPTIONS'字段内加入'builtins':['location of files containing filter'],比如:

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [TEMPLATE_DIR],
            'APP_DIRS': True,
            'OPTIONS': {
                # 'string_if_invalid':InvalidTemplateVariable('%s'),
                # 'autoescape':False,
                'context_processors': [
                    
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                    'banners.processors.onsale',
                    
                ],
                'builtins':['testBegin.common.testcommon'],
            },
        },
    ]
    

    写自定义的模板filter

    自定义filter是有1个或者2个参数的Python函数:

    • 输入变量的值(不一定必须是string)

    • 值的参数,可以有默认值

      比如,{{ var|foo:'bar' }},foo被掺入了变量var和参数'bar'。

      下面是个简单的例子:

      #bannerTags.py
      from django import template
      register=template.Library()
      
      @register.filter()
      def john(value,arg):
          return value.replace(arg,'john')
      
      {% load bannerTags %}
      {{ coffee|john:'m' }}
      

      coffee的值为'macomaco',

    @register.filter可以没有参数,直接用@register.filter来装饰,也可以有参数,起到特定的功能:

    • name:默认的filter的名字就是函数名字,而name参数可以改变filter的名字

    @register.filter(name='cutname')
    def cut(value, arg):
        return value.replace(arg, '')
    
    {{ coffee|cutname:'m' }}
    
    • 此外,还有is_safe,needs_autoscape,expects_localtime.

    字符期望的Filter(Template filters that expect strings)

    django.template.defaultfilters.stringfilter()

    在上面的例子中,如果coffee的值是11,则会报错:

    解决这个只需要再加层装饰器就可以了:

    from django import template
    from django.template.defaultfilters import stringfilter
    register=template.Library()
    @register.filter
    @stringfilter
    def john(value,arg):
        return value.replace(arg,'john')
    

    可以发现已经没有报错了:

    Filter 和 auto-escaping

    from django import template
    from django.template.defaultfilters import stringfilter
    register=template.Library()
    from django.utils.html import conditional_escape
    from django.utils.safestring import mark_safe
    
    @register.filter
    @stringfilter
    def john(value,arg):
        return value.replace(arg,'john')
    @register.filter(is_safe=True)
    def myfilter0(value):
        return value 
    @register.filter()
    def myfilter(value):
        return mark_safe(value)
    
    @register.filter(needs_autoescape=True)
    def testautoes(text,autoescape=True):
        first,other=text[0],text[1:]
        if autoescape:
            esc=conditional_escape
        else:
            esc=lambda x:x 
        result='<b>%s</b>%s' % (esc(first),esc(other))
        return mark_safe(result)
        # return result
    
    @register.filter()
    def myfilter1(value):
        value=conditional_escape(value)
        return mark_safe(value)
    
    @register.filter()
    def myfilter2(value):
        value=conditional_escape(value)
        return value
    
    @register.filter()
    def myfilter3(value):
        return value
    
    {% load bannerTags %}
    {{ coffee|john:'a' }}
    <br>
    {{ coffee|testautoes }}--testautoes
    <br>
    {{ coffee|myfilter }}--myfilter
    <br>
    {{ coffee|myfilter0 }}--myfilter0
    <br>
    {{ coffee|myfilter1 }}--myfilter1
    <br>
    {{ coffee|myfilter2 }}--myfilter2
    <br>
    {{ coffee|myfilter3 }}--myfilter3
    

    当在settings.py的Templates的'OPTIONS'添加autoexcape:False时,

    image-20200909200612382

    从这个例子可以看出几点:

    • 自定义filter的函数大多都受总的autoescape设定(即settings.py中关于autoescape)的影响,如果总的autoescape被设定为False,则含有”不安全“的符号如'<,>,'将不被转义,但是当通过conditional_escape处理后,则仍然被转义,具体表现为上图'myfilter1'和'myfilter2'
    • 当总的autoescape是True(默认情况),大多含有”不安全“的符号,都要进行转义,但是通过mark_safe而没有通过conditional_escape的处理,将直接被渲染。

    写自定义Tags

    Tags 比Filter更复杂,因为tags可以做任何事情,Django提供了很多快捷方式来使得写tags更方便。

    Simple tags

    django.template.Library.simple_tag()

    很多模板tag有很多参数,字符串或者模板变量,然后仅仅基于输入的参数进行一些操作,然后返回结果。比如,‘current_time'可以接受一个字符串模板,返回时间的格式化的字符。

    为了简化这种tag的创建过程,Django提供了个帮助函数,simple_tag,这个函数是django.template.Library的方法,它的接受任意的参数,用render函数包裹该函数,所以它就是起了一个装饰器的作用。

    #bannerTags.py
    import datetime
    from django import template
    
    register = template.Library()
    
    @register.simple_tag
    def current_time(format_string):
        return datetime.datetime.now().strftime(format_string)
    
    {% load bannerTags %} 
    {% current_time ' %Y/ %m/ %d' as tim %} 
    {{tim}}
    
    image-20200909232328124

    不像其他tag的用法,如果是autoescape模式,simple_tag就会将它的输出再输入conditional_escape()中,来保证正确的HTML,来免于XSS攻击。

    如果十分确定没有XSS攻击代码,则可以用mark_safe(),而对于创建小的HTML代码块,建议用format_html(),而不是mark_safe().

    • 如果需要重命名,老规矩,就像Filter一样,在register.simple_tag()中传入name即可。

    • 如果需要接受任意数量的positional argument或者keyword arguments,比如:

      @register.simple_tag
      def my_tag(a, b, *args, **kwargs):
          warning = kwargs['warning']
          profile = kwargs['profile']
          ...
          return ...
      

      在模板中,任意的参数,被空格分隔,就会被传入tag中。与python类似的是,对于keyword argument,用 等于号 '='来表示,而keyword argument跟在python中的类似,也必须放在position arguments之后。

      {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}
      
    • 如果不直接输出tag的结果,而是将该结果储存起来,也是可以的。通过as就可以达到这个效果,比如:

      {% current_time "%Y-%m-%d %I:%M %p" as the_time %}
      <p>The time is {{ the_time }}.</p>
      

      Inclusion tags

      django.template.Library.inclusion_tag()

      另一种常见的类型是通过render其他的模板来展示数据的,这种tags被称为'inclusion tags‘。

    写法步骤如下:

    (1)首先定义一个函数及其输入参数,然后返回一个字典,该字典将被当做一个template context传入被rendered 的其他模板。

    #testcommon.py
    def show_results(strings):
        choices=strings.split('s')
        return {'choices':choices}
    

    (2)定义被rendered的其他模板,其位置必须能被搜索到。

    # results.html
    <ul>
        {% for alpha in choices %}
        {% if alpha %}
        <li> {{ alpha }}</li>
        {% endif %}
        {% endfor %}
    </ul>
    

    (3)通过调用inclusion_tag来创建,和注册一个inclusion tag。

    #testcommon.py
    @register.inclusion_tag('banners/results.html')
    def show_results(strings):
        choices=strings.split('s')
        return {'choices':choices}
    

    (4)在模板中使用该tag。

    #test.html
    {% show_results coffee %}
    

    其路由为:

    urlpatterns=[
        url(r'^method',views.method,name='method'),
        url(r'^$',TemplateView.as_view(template_name='banners/test.html',extra_context={'coffee':'moscamsocas'}),name='index'),
    ]
    

    运行:

    image-20200910130951792
    • 也可以在模板中直接传字符串:

      #test.html
      {% show_results 'asbscs' %}
      
    image-20200910131132382

    还有一种等效的方法是:

    #testcommon.py
    from django.template.loader import get_template
    t=get_template('banners/results.html')
    register.inclusion_tag(t)(show_results)
    

    可以看到,本质上与第一种方法是一样的,只不过是显式的找到html然后传递给register.inclusion_tag,然后再传入show_results,这个过程本身就是装饰器的显式表达。

    有时候,inclusion tags需要将大量的参数传入,这时候对于使用tags的人来说非常痛苦,还要记住参数顺序。为解决这种问题,Django为inclusion tag提供了take_context选项,当表明了它,并设置为True,使用模板时,可以不需要参数,相应的python function只需要传入一个参数context,从而达到直接将被调用的template的context_dict传入tag function,并通过tag function 传入被rendered的Html。

    比如:

    @register.inclusion_tag('banners/results.html',takes_context=True)
    def show_results(context):
        choices=context['coffee'].split('s')
        return {'coffee':choices }
    
    #被call的test.html
    {% show_results  %}
    
    #被rendered的html,可见coffee被传入
    <ul>
        {% for alpha in coffee %}
        {% if alpha %}
        <li> {{ alpha }}</li>
        {% endif %}
        {% endfor %}
    </ul>
    

    image-20200910134347726

    最后,inclusion_tag也可以接受任意数量的position argument和keyword argument:

    如:

    @register.inclusion_tag('my_template.html')
    def my_tag(a, b, *args, **kwargs):
        warning = kwargs['warning']
        profile = kwargs['profile']
        ...
        return ...
    
    {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}
    
    ##### 愿你一寸一寸地攻城略地,一点一点地焕然一新 #####
  • 相关阅读:
    八数码难题 (codevs 1225)题解
    小木棍 (codevs 3498)题解
    sliding windows (poj 2823) 题解
    集合删数 (vijos 1545) 题解
    合并果子 (codevs 1063) 题解
    等价表达式 (codevs 1107)题解
    生理周期 (poj 1006) 题解
    区间 (vijos 1439) 题解
    区间覆盖问题 题解
    种树 (codevs 1653) 题解
  • 原文地址:https://www.cnblogs.com/johnyang/p/13645212.html
Copyright © 2011-2022 走看看