django自定義模板標籤和過濾器

Django雖然爲咱們內置了二十多種標籤和六十多種過濾器,可是需求是各類各樣的,總有一款你cover不到。Django爲咱們提供了自定義的機制,能夠經過使用Python代碼,自定義標籤和過濾器來擴展模板引擎,而後使用{% load %}標籤。html

1、前置步驟

Django對於自定義標籤和過濾器是有前置要求的,首先一條就是代碼佈局和文件組織。django

你能夠爲你的自定義標籤和過濾器新開一個app,也能夠在原有的某個app中添加。服務器

無論怎麼樣,第一步,在app中新建一個templatetags(名字固定,不能變,只能是這個),和views.py、models.py等文件處於同一級別目錄下。這是一個包!不要忘記建立__init__.py文件以使得該目錄能夠做爲Python的包。app

在添加templatetags包後,須要從新啓動服務器,而後才能在模板中使用標籤或過濾器。函數

  • 將你自定義的標籤和過濾器將放在templatetags包下的一個模塊裏。佈局

  • 這個模塊的名字是後面載入標籤時使用的標籤名,因此要謹慎的選擇名字以防與其餘應用下的自定義標籤和過濾器名字衝突,固然更不能與Django內置的衝突。spa

假設你自定義的標籤/過濾器在一個名爲poll_extras.py的文件中,那麼你的app目錄結構看起來應該是這樣的:code

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

爲了讓{% load xxx %}標籤正常工做,包含自定義標籤的app必須在INSTALLED_APPS中註冊。而後你就能夠在模板中像以下這樣使用:orm

{% load poll_extras %}

在templatetags包中放多少個模塊沒有限制。只須要記住{% load xxx %}將會載入給定模塊名中的標籤/過濾器,而不是app中全部的標籤和過濾器。htm

要在模塊內自定義標籤,首先,這個模塊必須包含一個名爲register的變量,它是template.Library的一個實例,全部的標籤和過濾器都是在其中註冊的。 因此把以下的內容放在你的模塊的頂部:

from django import template register = template.Library() 

友情提示:能夠閱讀Django的默認過濾器和標記的源代碼。它們分別位於django/template/defaultfilters.pydjango/template/defaulttags.py中。它們是最好的範例!

2、自定義模板過濾器

1. 編寫過濾器

自定義過濾器就是一個帶有一個或兩個參數的Python函數

注意:這個Python函數的第一個參數是你要過濾的對象,第二個參數纔是你自定義的參數。並且最多總共只能有兩個參數,因此你只能自定義一個參數!這是過濾器的先天限制。

  • 變量的值:不必定是字符串形式。
  • 參數的值:能夠有一個初始值,或者徹底不要這個參數。

例如,在{{ var|foo:"bar" }}中,foo過濾器應當傳入變量var和參數"bar"。

因爲模板語言沒有提供異常處理,任何從過濾器中拋出的異常都將會顯示爲服務器錯誤。

下面是一個定義過濾器的例子:

def cut(value, arg): """將value中的全部arg部分切除掉""" return value.replace(arg, '') 

下面是這個過濾器的使用方法:

{{ somevariable|cut:"0" }}

大多數過濾器沒有參數,在這種狀況下,你的過濾器函數不帶額外的參數便可,但基本的value參數是必帶的。例如:

def lower(value): # Only one argument.
    """Converts a string into all lowercase"""
    return value.lower()

2. 註冊過濾器

類原型:django.template.Library.filter()

一旦你寫好了過濾器函數,就須要註冊它,方法是調用register.filter,好比:

register.filter('cut', cut)
register.filter('lower', lower)

Library.filter()方法須要兩個參數:

  • 過濾器的名稱:一個字符串對象
  • 編譯的函數 :你剛纔寫的過濾器函數

還能夠把register.filter()用做裝飾器,以以下的方式註冊過濾器:

@register.filter(name='cut') def cut(value, arg): return value.replace(arg, '') @register.filter def lower(value): return value.lower() 

上面第二個例子沒有聲明name參數,Django將使用函數名做爲過濾器的名字。

自定義過濾器就是這麼簡單,使用起來也和普通的過濾器沒什麼區別。咱們用Python的方式解決了HTML的問題。

3、自定義模板標籤

標籤比過濾器更復雜,由於標籤能夠作任何事情。Django提供了大量的快捷方式,使得編寫標籤比較容易。 對於咱們通常的自定義標籤來講,simple_tag是最重要的,它幫助你將一個Python函數註冊爲一個簡單的模版標籤。

1. simple_tag

原型:django.template.Library.simple_tag()

爲了簡單化模版標籤的建立,Django提供一個輔助函數simple_tag,這個函數是django.template.Library的一個方法。

好比,咱們想編寫一個返回當前時間的模版標籤,那麼current_time函數從而能夠這樣寫︰

import datetime from django import template register = template.Library() @register.simple_tag def current_time(format_string): return datetime.datetime.now().strftime(format_string) 

關於simple_tag函數有幾件值得注意的事項︰

若是不須要額外的轉義,可使用mark_safe()讓輸出不進行轉義,前提是你絕對確保代碼中不包含XSS漏洞。 若是要建立小型HTML片斷,強烈建議使用format_html()而不是mark_safe()

若是你的模板標籤須要訪問當前上下文,能夠在註冊標籤時使用takes_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)

請注意,第一個參數必須稱做context!

若是你須要重命名你的標籤,能夠給它提供自定義的名稱︰

register.simple_tag(lambda x: x - 1, name='minusone') @register.simple_tag(name='minustwo') def some_function(value): return value - 2 

simple_tag函數能夠接受任意數量的位置參數和關鍵字參數。 像這樣:

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

而後在模板中,能夠將任意數量的由空格分隔的參數傳遞給模板標籤。像在Python中同樣,關鍵字參數的值使用等號("=")賦予,而且必須在位置參數以後提供。 例子:

{% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}

能夠將標籤結果存儲在模板變量中,而不是直接輸出。這是經過使用as參數後跟變量名來實現的:

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

2. inclusion_tag()

原型:django.template.Library.inclusion_tag()

另外一種常見類型的模板標籤是經過渲染一個模板來顯示一些數據。例如,Django的Admin界面使用自定義模板標籤顯示"添加/更改"表單頁面底部的按鈕。這些按鈕看起來老是相同,但連接的目標倒是根據正在編輯的對象而變化的。

這種類型的標籤被稱爲"Inclusion 標籤"。

下面,展現一個根據給定的tutorials中建立的Poll對象輸出一個選項列表的自定義Inclusion標籤。在模版中它是這麼調用的:

{% show_results poll %}

而輸出是這樣的:

<ul>
  <li>First choice</li> <li>Second choice</li> <li>Third choice</li> </ul> 

具體的編寫方法:

首先,編寫Python函數:

def show_results(poll): choices = poll.choice_set.all() return {'choices': choices} 

接下來,建立用於標籤渲染的模板results.html︰

<ul>
{% for choice in choices %} <li> {{ choice }} </li> {% endfor %} </ul> 

最後,經過調用Library對象的inclusion_tag()裝飾器方法建立並註冊Inclusion標籤︰

@register.inclusion_tag('results.html')
def show_results(poll):
    ...

或者使用django.template.Template實例註冊Inclusion標籤︰

from django.template.loader import get_template t = get_template('results.html') register.inclusion_tag(t)(show_results) 

inclusion_tag函數能夠接受任意數量的位置參數和關鍵字參數。像這樣:

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

而後在模板中,能夠將任意數量的由空格分隔的參數傳遞給模板標籤。像在Python中同樣,關鍵字參數的值的設置使用等號("=") ,而且必須在位置參數以後提供。例如:

{% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}

能夠在標籤中傳遞上下文中的參數。好比說,當你想要將上下文context中的home_linkhome_title這兩個變量傳遞給模版。 以下所示:

@register.inclusion_tag('link.html', takes_context=True) def jump_link(context): return { 'link': context['home_link'], 'title': context['home_title'], } 

注意函數的第一個參數必須叫作context。context必須是一個字典類型。

register.inclusion_tag()這一行,咱們指定了takes_context=True和模板的名字。模板link.html很簡單,以下所示:

Jump directly to <a href="{{ link }}">{{ title }}</a>. 

而後,當任什麼時候候你想調用這個自定義的標籤時,只須要load它自己,不須要添加任何參數,{{ link }}{{ title }}會自動從標籤中獲取參數值。像這樣:

{% jump_link %}

使用takes_context=True,就表示不須要傳遞參數給這個模板標籤。它會本身去獲取上下文。

相關文章
相關標籤/搜索