遞歸引用Django模板實現渲染樹形結構

2024年2月6日 21点热度 0人点赞

在web開發中,經常需要顯示展示層次化的數據,比如文件目錄、產品分類目錄、組織結構等,這是典型的樹形數據結構。Django是web開發中使用非常廣泛的框架,那麼你知道怎麼使用Django模塊渲染樹形數據結構嗎?本文將會給出答案。

我們知道編程語言大多都有遞歸調用的能力,遍歷樹形數據結構往往都需要進行遞歸調用,而Django使用模板引擎進行頁面的渲染,而樹形結構數據往往都是從數據庫動態獲取的,嵌套的層級深度並不是確定的,因此Django模板必須能夠做到類似遞歸調用的能力才能做到渲染樹形數據結構。幸運地的是,Django模板引擎是支持遞歸引用的,接下來我們通過一個簡單的例子來詳細介紹Django模板如何通過include指令遞歸引用模塊文件,最終渲染出樹形目錄結構。這裡不會從頭介紹Django如何創建項目,隻針對關鍵文件進行講解,因此需要讀者具備一定的Django基礎。

準備樹形結構數據

一般情況下,樹形結構數據都是從數據庫動態生成的,為了突出重點,這裡僅給出最終的樹形數據結構示例。我們把它寫在views.py文件中,tree_list為樹形數據,列表中的每一項包含id、name、parent_id、children,其中children為子節點,包含同樣的結構,可以一直嵌套下去。

from django.shortcuts import render
# Create your views here.
def index(request):
    """
    tree_list數據結構形式:[
        {
            "id": xx, 
            "name": xxx, 
            "parent_id": xxx, 
            "children": [
                {
                    "id": xx,
                    "name": xxx, 
                    "parent_id": xxx, 
                    "children": []
                }
            ]
        },
        ...
    ]
    """
    context = {}
    context["tree_list"] =  [{
                "id": 2,
                "name": "類別A",
                "parentId": None,
                "children": [
                    {
                        "id": 8,
                        "name": "類別A1",
                        "parentId": 2,
                        "children": [
                            {
                                "id": 137,
                                "name": "類別A11",
                                "parentId": 8,
                            }
                        ]
                    },
                    {
                        "id": 221,
                        "name": "類別A2",
                        "parentId": 2,
                    }
                ]
            },
            {
                "id": 52,
                "name": "類別B",
                "parentId": None,
                "children": [
                    {
                        "id": 54,
                        "name": "類別B1",
                        "parentId": 52,
                        "fileCount": 10,
                        "children": [
                            {
                                "id": 55,
                                "name": "類別B11",
                                "parentId": 54,
                                "children": [
                                    {
                                        "id": 56,
                                        "name": "類別B111",
                                        "parentId": 55,
                                        "children": [
                                            {
                                                "id": 57,
                                                "name": "類別B1111",
                                                "parentId": 56,
                                                "children": [
                                                    {
                                                        "id": 58,
                                                        "name": "類別B11111",
                                                        "parentId": 57,
                                                    }
                                                ]
                                            }
                                        ]
                                    }
                                ]
                            }
                        ]
                    }
                ]
            },
            {
                "id": 53,
                "name": "類別C",
                "parentId": None,
                "children": [
                    {
                        "id": 80,
                        "name": "類別C1",
                        "parentId": 53,
                    },
                    {
                        "id": 224,
                        "name": "類別C2",
                        "parentId": 53,
                    }
                ]
            },
            {
                "id": 69,
                "name": "類別D",
                "parentId": None,
                "children": [
                    {
                        "id": 70,
                        "name": "類別D1",
                        "parentId": 69,
                        "children": [
                            {
                                "id": 4,
                                "name": "類別D11",
                                "parentId": 70,
                                "children": [
                                    {
                                        "id": 51,
                                        "name": "類別D111",
                                        "parentId": 4,
                                    }
                                ]
                            }
                        ]
                    },
                    {
                        "id": 91,
                        "name": "類別D2",
                        "parentId": 69,
                    },
                    {
                        "id": 102,
                        "name": "類別D3",
                        "parentId": 69,
                    },
                    {
                        "id": 113,
                        "name": "類別D4",
                        "parentId": 69,
                    },
                    {
                        "id": 121,
                        "name": "類別D5",
                        "parentId": 69,
                    },
                    {
                        "id": 136,
                        "name": "類別D6",
                        "parentId": 69,
                    },
                    {
                        "id": 140,
                        "name": "類別D7",
                        "parentId": 69,
                        "children": [
                            {
                                "id": 142,
                                "name": "類別D71",
                                "parentId": 140,
                            }
                        ]
                    }
                ]
            }
        ]
    return render(request, 'demo/index.html', context)

模板文件

templates/demo目錄下創建兩個模板文件index.html、children.html,index.html文件中關鍵部分的代碼為for循環指令包圍的部分,它負責遍歷上面提到的tree_list列表的每一項,也就是數據結構的第一級目錄,如果列表中的某一項children內容不為空,則執行指令{% include 'demo/children.html' with tree_list=item.children %},它的意思就相當於render(request, 'demo/children.html', item.children),也就是說插入當前項的子節點作為數據源渲染出的頁面。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>django模板</title>
    <style>
        .flex {
            display: flex;
        }
        .list-unstyled ul {
            padding-left: 0;
            list-style: none;
        }
        .tree {
            padding: 0.3rem 1rem ;
            background-color: #f5f5f5;
            color: #333;
        }
        .tree li li {
            padding-left: 0.5rem;
        }
        .tree li::before {
            content: '\0203A';
            opacity: 0;
        }
        .tree .expand::before {
            content: '\0203A';
            opacity: 1;
        }        
    </style>
</head>
<body>
    <h3>Django模板渲染樹形目錄示例</h3>
    <div class="flex">
        <div class="tree list-unstyled">
            <ul>
                {% for item in tree_list %}
                    {% if item.children %}
                    <li class="expand">
                        {{ item.name }}
                        {% include 'demo/children.html' with tree_list=item.children %}
                    </li>
                    {% else %}
                    <li>
                        {{ item.name }}
                    </li>
                    {% endif %}
                {% endfor %}
            </ul>
        </div>
        <div class="right"></div>
    </div>
</body>
</html>

接下來看children.html,它看起來和前面是很類似的,隻不過這裡include指令中使用的模板就是自己本身,傳入的數據源逐層剝離出子節點,這就是和編程語言的遞歸是一樣的了,最終所有children節點都完全遍歷到並渲染出最終的html頁面,這樣就實現了渲染樹形結構數據。

<ul>
    {% for item in tree_list %}
        {% if item.children %}
        <li class="expand">
            {{ item.name }}
            {% include 'demo/children.html' with tree_list=item.children %}
        </li>
        {% else %}
        <li>
            {{ item.name }}
        </li>
        {% endif %}
    {% endfor %}
</ul>

最後渲染出的樹形目錄如下, index.html中寫了一點css改變了默認的樣式,你可以根據自己的需要使用成熟的UI框架來定制樹形目錄的樣式。本文到這裡就結束了,希望能幫助到有需要的朋友,也歡迎大傢多多關註我的公眾號【一點鑫得】,我將持續輸出有價值的內容。