djanngo 自定義標籤和過濾器

 Django支持自定義標籤和過濾器。起初還不太重視它這項功能,但最近試了試自定義標籤。發現django這個功能實在是太爽了。

 

首先在你項目的一個app中建立一個python源文件夾,(即文件夾裏面要包含一個__init__.py.)文件夾名爲templatetags.  此文件夾便是存放自定義標籤和過濾器的源碼的地方了。

 

再如果是在templatetags文件夾中定義了標籤,如 test_tags.py,要如何使用我們自定義的test_tags.py呢。很簡單,只要在django的模板中加入{% load test_tags %},在test_tags.py 源文件中的自定義標籤就可以在有load語句的模板中使用了。

 

下面詳細的來談談建立自定義標籤的過程以及方法。

 

1.建立項目,app的不說。只要在隨意一個app中建立上文提到的templatetags文件夾。

這裏是有點不理解的地方,在任意一個app建立的tags別的app能夠使用嗎?起初對此很疑惑。以爲在一個app下建立的tags就這一個app能使用。爲了大家都能夠使用自己定義的tags,我還想把templatetags單獨拿出來,跟普通app在項目當中是平級地位。這種思路搞了很久發現行不通。無奈只好打算使用copy在每一個app都複製一份templatetags。(當然這只是我起初的錯誤想法)但後來發現,居然一個app中有,其他的app中就可以直接使用此自定義的文件了。只需要在需要的模板當中(不管模板是在你的那個app中)調用load語句將自定義的文件load進來便可以。

隨後看了看一些文檔,只要templatetags所在位置是settings.py中INSTALLED_APPS中配置過的,或是在TEMPLATE_DIRS配置過的,任意一個位置便可以。

一定記得要在templatetags文件夾中包含__init__.py文件。空文件便可。

 

2.編寫自定義文件代碼test_tags.py。

 

現看看簡單一點的過濾器(filter).

我的一個簡單代碼如下

#!/usr/bin/env python
#coding:utf-8

from django import template

register = template.Library()

def percent_decimal(value):

value = float(str(value))
value = round(value, 3)
value = value * 100

return str(value) + '%'
register.filter('percent_decimal', percent_decimal) 


以上代碼的意思是將傳過來的小數轉換成百分比顯示。(django自帶一個widthratio標籤頁可以完成此問題,但它的誤差太大,小數位直接截掉了)。

其中register = template.Library(),register.filter('percent_decimal', percent_decimal) 兩句是將所寫代碼註冊到能用標籤。最後一句的register.filter('percent_decimal', percent_decimal) 。第一個參數是字符串,就只在模板中使用時候的字符串比如{{12.09|percent_decimal}},名字可以自由取得。後面一個參數接受一個函數名,這個便是上面自定義的percent_decimal方法了。此方法中的value參數便是從模板中傳遞過來的參數。比如{{12.09|percent_decimal}}, percent_decimal方法接受到的value參數便是
12.09.

 

再來看看如何自定義tags。

自定義tags相對自定義filter要複雜一點。但仔細研究的話難度也不大。

自定義tags最基本的格式如下

<pre name="code" class="python">from django import template

register = template.Library()

class TestNode(template.Node):
    def __init__(self):
        pass

    def render(self, context):
        return "xxxxx"

def test(parser, token):
    return TestNode()

register.tag('my_tag', test)


 其中包的引入和註冊跟filter類型。主要的功能代碼是一個類(繼承自template.Node,所有的自定義tags都必須從這個類繼承)和一個方法,以上代碼什麼功能都沒有做,但我們要先搞明白它的實現機制。

如果從模板中使用以上的自定義tags,比如{% my_tag aaa.bbb %},則會調用上面代碼註冊的test方法,其中test方法中有兩個參數,一個是parser,這個作用是挺大的,稍後說明。另一個是token,這個便是從模板當中使用的這個標籤中所包含的所有字符串。如果使用的是{{ my_tag aaa.bbb }}則,token的值爲“my_tag aaa.bbb”。注意是字符串類型。

隨後調用TestNode類,當中有個render方法,其中有個參數是context,這個context參數就是在執行模板的渲染時由 View 傳入的,在這裏跟在被渲染的模板中可以調用同樣的變量,可以試驗在render中打印 "print user",便顯示的是登陸用戶的信息。

 

大概明白了自定義tags的實現機制後。看一個複雜一點點的代碼。 

 

class PermissionLevel(template.Node):
    
    """
    根據級別返回相應值
    """    
    
    def __init__(self, sequence, text_level):
        self.sequence = sequence
        self.text_level = text_level
        pass
    def render(self, context):
        userInfo = context['user']
        level = 4  #拿到用戶級別
        
        values = self.sequence.resolve(context, True)
        if self.text_level <= level:
            return str(values)
        else:
             return 'xxx'

def do_permission_level(parser, token):
        
    try:
        tag_name, text_name, text_level = token.split_contents()
    except:
        raise template.TemplateSyntaxError, \
                   "%r 標籤語法錯誤,後面參數爲兩位,分別爲變量名和該變量信息隱私等級" % \
                    token.split_contents[0]
    try:
        text_level = int(text_level)
    except:
        raise TemplateSyntaxError, "permission_level標籤語法錯誤,信息隱私等級應爲整型數字"
    sequence = parser.compile_filter(text_name)
    
    return PermissionLevel(sequence, text_level)
     
register.tag('permission_level', do_permission_level)

以上的代碼完成的功能是判定一個信息的隱私級別,再根據登陸用戶的權限來區分顯示或不顯示。在模板中的用法如下

{% permission_level  objects.count  4 %}

其中permission_level  爲標籤名,objects.count是從view中傳遞過來的變量或屬性。後面的 4 便是此屬性(objects.count)的隱私級別爲4,只有登錄用戶隱私級別大於它的時候纔會顯示,否則顯示爲“xxx”。

 

下面仔細來分析一下上面的代碼。

tag_name, text_name, text_level = token.split_contents()
這一句將{% permission_level  objects.count  4 %}分解成三個字符,其中text_name爲“objects.count ”。這裏跟自定義的filter不同,filter 是可以接受非字符的參數的,類型{{ 0.123|floatformat:2 }}也可以是{{ 0.123|floatformat:“2” }},後面的2參數加了引號。其中如果不加引號的話便傳遞的是非字符對象,像模板中的user是可以通過filter傳遞過去的。但tags不同,tag傳遞給註冊的函數中的參數是字符串。

 

當時想到做自定義的tags也是項目需要。但自己開始做的時候發現tag傳遞的是字符串,當時也查了不少資料沒找到解決的方法,那時就想放棄自定義tags了。但注意到django自帶的tags不也是傳遞字符嗎,像{%if user%}, {% for item in test_list %}這樣的,django肯定有什麼方法可以將類似“user”, "test_list"這樣的字符和view中傳遞來的對象關聯起來。於是便開始硬着頭皮看django的源碼了。(要知道對於像我這樣的菜鳥來說。看源碼可不是容易的事情)。

 

到底django是用什麼魔法做到字符變對象的呢。起先我以爲是這樣的,比如{% if user  %},則先提取user取來,再將傳遞過去的user從context中取去,user=context['user'],但後來一想,單個的對象好想是可以,但如果有句點符號的是怎麼做到的呢。就像user.username?難道是再將它通過字符串操作再分離一次,但如果遇到多個句點符號呢?類似

user.profile.realname。照這個思路看源碼,發現不是我想的這樣的。有些源代碼沒看太懂,但正是這些沒看懂的代碼纔是關鍵。

 

再回頭看看上面的代碼,註冊標籤的代碼方法中有個parser參數,正是這個參數。

對照上面的代碼。

sequence = parser.compile_filter(text_name)
將開始從token.split_contents()中分離出來的 像:user,,user.age ,  user.profile.realname等字符串先編譯成一個sequence 對象。怎麼編譯的我也不太明白,不去管它了。

隨後在類方法裏面有句values = self.sequence.resolve(context, True)
再打印出values來看看,type(values)看看,哈哈。不再是字符串了,是真正的實例對象或是變量。

 

那麼一切OK。雖然不知道Django中這兩句是怎麼樣實現的,但不管了。至少現在我們可以自由的定製適合的filter和tags了。

轉載地址 :http://xiao80xiao.iteye.com/blog/519394

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章