在設計系統時,難免會遇到有樹狀階層的情況,那要怎麼在有限的資料表格欄位裡去做出階層呢?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/ 就可以看到結果了。
django-mptt 不光只是上面提到的這些,他還包括了 admin 、 form 、查詢等等的處理,是一整套的解決方案。如果你有使用到階層性的資料關係,可以直接考慮使用 django-mptt 來做快速開發。
上面範例的完整程式碼可以在這裡找到:https://github.com/elleryq/ithome-iron-2020-django/tree/day-11