iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 19
0
Modern Web

加速你的 Django 網站開發 - Django 的好用套件系列 第 19

19. django-mptt

在設計系統時,難免會遇到有樹狀階層的情況,那要怎麼在有限的資料表格欄位裡去做出階層呢?django-mptt 提供了這樣的便利,除了可以描述樹狀階層,也提供了相關的函數去查詢跟訪問節點。

MPTT 是 Modified Preorder Tree Traversal 的縮寫,

專案文件網址:https://django-mptt.readthedocs.io/en/latest/

安裝

poetry add django-mptt

設定

INSTALLED_APPS = (
    'django.contrib.auth',
    # ...
    'mptt',
)

使用

首先我們先建立一個 app

poetry run python manage.py startapp treemenu

撰寫 model,我們把名稱定為 MenuItem,讓它繼承 MPTTModel 跟定義 parent, name 就可以了。

# treemenu/models.py
from django.db import models
from mptt.models import MPTTModel, TreeForeignKey

class MenuItem(MPTTModel):
    name = models.CharField(max_length=50, unique=True)
    parent = TreeForeignKey(
        'self',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='children')

    class MPTTMeta:
        order_insertion_by = ['name']

在繼承 MPTTModel 以後,MenuItem model 會多出 level, lft, rght, tree_id 這幾個欄位,這些欄位是用來輔助描述階層關係的。

那要怎麼建立階層關係呢?很簡單,在建立時,指定 parent :

# 開啟 shell 來進行
# poetry run python manage.py shell
from treemenu.models import MenuItem

rootitem = MenuItem.objects.create('root')
rootitem = MenuItem.objects.create(name='root')
item1 = MenuItem.objects.create(name='L1', parent=rootitem)
item2 = MenuItem.objects.create(name='L2', parent=rootitem)
item3 = MenuItem.objects.create(name='L3', parent=rootitem)
item11 = MenuItem.objects.create(name='L1_1', parent=item1)
item12 = MenuItem.objects.create(name='L1_2', parent=item1)
item13 = MenuItem.objects.create(name='L1_3', parent=item1)
item21 = MenuItem.objects.create(name='L2_1', parent=item2)
item22 = MenuItem.objects.create(name='L2_2', parent=item2)

有了資料,接下來該怎麼呈現在 View 裡呢?

首先我們先定義 View

# treemenu/views.py
from django.shortcuts import render
from django.views.generic import TemplateView
from .models import MenuItem

class TreeView(TemplateView):
    template_name = 'treemenu/treeview.html'
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        rootmenu = MenuItem.objects.filter(parent__isnull=True).first()
        context.update({
            'rootmenu': rootmenu.get_descendants(),
        })
        return context

Template 裡,我們可以使用 django-mptt 所提供的 template tag 來幫忙

{# treemenu/templates/treemenu/treeview.html #}
{% load mptt_tags %}
<ul>
    {% recursetree genres %}
        <li>
            {{ node.name }}
            {% if not node.is_leaf_node %}
                <ul class="children">
                    {{ children }}
                </ul>
            {% endif %}
        </li>
    {% endrecursetree %}
</ul>

最後在 urls 加入這個 View

# urls.py
from treemenu.views import TreeView

# ...
urlpatterns = [
  # ...
  # treeview
  path('tree/', TreeView.as_view()),
]

打開瀏覽器,在網址列輸入 http://localhost:8000/tree/ 就可以看到結果了。

https://ithelp.ithome.com.tw/upload/images/20200919/20012434OMJLjEcdER.png

結語

django-mptt 不光只是上面提到的這些,他還包括了 admin 、 form 、查詢等等的處理,是一整套的解決方案。如果你有使用到階層性的資料關係,可以直接考慮使用 django-mptt 來做快速開發。

上面範例的完整程式碼可以在這裡找到:https://github.com/elleryq/ithome-iron-2020-django/tree/day-11


上一篇
18. django-crispy-forms
下一篇
20. pytest-django
系列文
加速你的 Django 網站開發 - Django 的好用套件30

尚未有邦友留言

立即登入留言