1. 程式人生 > >Django 框架中的自定義模板標籤(template.Library())

Django 框架中的自定義模板標籤(template.Library())

某一些標籤(例如:選單欄、css、JS、以及一些複雜計算後的資料等)需要我們自定義。

然後再在指定的html中引用並顯示。

之所以要用到標籤,主要作用就是想讓一些內容在多個模板(HTML)中都要有,比如選單欄。

我們絕對不想在每個檢視函式(views中)都寫一次這些變數內容。

即每個頁面都需要用到info,若,每個檢視函式都寫一次,那真的是所非常痛苦,所以要說一下Django的上下文渲染器。

例如:(程式碼取自自強學堂

from django.shortcuts import render
 
def home(request):
    return render(request, 'home.html', {'info': 'Welcome to ziqiangxuetang.com !'})

在Html中使用就要這樣寫:

{{ info }}

1.首先建立一個py檔案,名字為myTag.py,然後引入template包

from django import template
#註冊我們自定義的標籤,只有註冊過的標籤,系統才能認識你,這是固定寫法
register = template.Library()

2.新增標籤相關的方法:

標籤相關方法指的是在html顯示前,後臺先進行預處理,和我們平常的方法相同,只不過這個方法是針對標籤所定義的:

# 使用者請求views中的檢視函式後,在試圖函式中呼叫的方法,即在session中寫入選單-許可權相關資料
def get_structure_data(request):
    """處理選單結構"""
    menu = request.session[settings.SESSION_MENU_KEY]
    all_menu = menu[settings.ALL_MENU_KEY]
    permission_url = menu[settings.PERMISSION_MENU_KEY]

    # 定製資料結構
    all_menu_dict = {}
    for item in all_menu:
        item['status'] = False
        item['open'] = False
        item['children'] = []
        all_menu_dict[item['id']] = item


    request_rul = request.path_info

    for url in permission_url:
        # 新增兩個狀態:顯示 和 展開
        url['status'] = True
        pattern = url['url']
        if re.match(pattern, request_rul):
            url['open'] = True
        else:
            url['open'] = False

        # 將url新增到選單下
        all_menu_dict[url['menu_id']]["children"].append(url)

        # 顯示選單:url 的選單及上層選單 status: true
        pid = url['menu_id']
        while pid:
            all_menu_dict[pid]['status'] = True
            pid = all_menu_dict[pid]['parent_id']

        # 展開url上層選單:url['open'] = True, 其選單及其父選單open = True
        if url['open']:
            ppid = url['menu_id']
            while ppid:
                all_menu_dict[ppid]['open'] = True
                ppid = all_menu_dict[ppid]['parent_id']

    # 整理選單層級結構:沒有parent_id 的為根選單, 並將有parent_id 的選單項加入其父項的chidren內
    menu_data = []
    for i in all_menu_dict:
        if all_menu_dict[i]['parent_id']:
            pid = all_menu_dict[i]['parent_id']
            parent_menu = all_menu_dict[pid]
            parent_menu['children'].append(all_menu_dict[i])
        else:
            menu_data.append(all_menu_dict[i])

    return menu_data
#將資料庫中指定使用者可顯示的選單-許可權進行html拼裝
def get_menu_html(menu_data):

    option_str2 = '''
                <li class="treeview {active}">
                    <a href="#"><i class="fa fa-link"></i><span>{menu_title}</span>
                      <span class="pull-right-container">
                        <i class="fa fa-angle-left pull-right"></i>
                      </span>
                    </a>
                    <ul class="treeview-menu">
                       {sub_menu}
                    </ul>
                </li>
        
    
    '''
    url_str2 = """
		    <li class="{active}"><a href="{permission_url}"><i class="fa"></i><i class="fa fa-circle-o"></i><span>{permission_title}</span></a></li>
    """

    menu_html = ''
    for item in menu_data:
        if not item['status']:  # 如果使用者許可權不在某個選單下,即item['status']=False, 不顯示
            continue
        else:
            if item.get('url'):  # 說明迴圈到了選單最裡層的url
                menu_html += url_str2.format(permission_url=item['url'],
                                             active="active" if item['open'] else "",
                                             permission_title=item['title'])
            else:
                if item.get('children'):
                    sub_menu = get_menu_html(item['children'])
                else:
                    sub_menu = ""

                menu_html += option_str2.format(menu_title=item['title'],
                                                sub_menu=sub_menu,
                                                active="active" if item['open'] else "",)

    return menu_html

以上程式碼和csdn上的一個寫rbac許可權相關的作者程式碼類似,本人對其進行了再加工。

django rbac原作者地址


3.新增自定義標籤,選單欄、css、js、登入使用者的使用者名稱都需要多頁面使用。

所以定義了下面標籤方法(本人未使用下面的css和js標籤)

@register.simple_tag
def rbac_menu(request):
    """
    顯示多級選單:請求過來 -- 拿到session中的選單,許可權資料 -- 處理資料 -- 作顯示
    返回多級選單:資料處理部分抽象出來由單獨的函式處理;渲染部分也抽象出來由單獨函式處理
    :param request: 
    :return: 
    """
    menu_data = get_structure_data(request)
    menu_html = get_menu_html(menu_data)
    return mark_safe(menu_html)


@register.simple_tag
def rbac_css():
    """
    rabc要用到的css檔案路徑,並讀取返回;注意返回字串用mark_safe,否則傳到模板會轉義
    :return: 
    """
    css_path = os.path.join('rbac', 'style_script', 'rbac.css')
    css = open(css_path, 'r', encoding='utf-8').read()
    return mark_safe(css)


@register.simple_tag
def rbac_js():
    """
    rabc要用到的js檔案路徑,並讀取返回
    :return: 
    """
    js_path = os.path.join('rbac', 'style_script', 'rbac.js')
    js = open(js_path, 'r', encoding='utf-8').read()
    return mark_safe(js)


@register.simple_tag
def rbac_loginUser(request):
    return mark_safe(request.session['name'])

4.在base.html中使用上述標籤.

下面body中的內容就是我們所想要的內容,其他頁面只要繼承base.html就都具有了該標籤的資料:

{% load myTag%}
{% load staticfiles %}
{#<!DOCTYPE html>#}
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <title>{% block title %}XXXXX{% endblock %}</title>

    {% block head_link %}{% endblock %}
    <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">

    <link rel="stylesheet" href="{% static '/bower_components/bootstrap/dist/css/bootstrap.min.css' %}">
    <!-- 子模板的樣式表應放在父模板的樣式表之後,只有這樣才可以在子模板中重定義父模板中的某些樣式 -->
    {% block styles %}{% endblock %}
</head>
<body>
    {% rbac_loginUser request %}
    {% rbac_menu request %}
</body>
</html> 

其他頁面要繼承時,加上下面的語句即可:

{% extends "base.html" %}
上述程式碼整合了多個csdn博主的程式碼,特此宣告下。