django-page-cms 已经使用django-mptt 解决了这类问题,所以我建议看看那里的处理方式。
具体来说,template to render the tree menu 是通过称为 pages_dynamic_tree_menu 的 custom template tag 递归包含的。
这听起来很复杂(自定义 ta-whaaa!),所以我会再分解一下。
我们可以认为Page in django-page-cms 与您的Section 类大致相同。
在 99% 的页面之间共享的基本模板中,django-page-cms gets you to insert the following snippet:
<ul>
{% for page in pages_navigation %}
{% pages_dynamic_tree_menu page %}
{% endfor %}
</ul>
pages_navigation is set by context processor(一种为 django 项目中的所有视图添加项目到 template context 的方法)并包含顶级页面(即那些没有父级的页面)。
pages_dynamic_tree_menu标签的定义和注册如下:
def pages_dynamic_tree_menu(context, page, url='/'):
"""
Render a "dynamic" tree menu, with all nodes expanded which are either
ancestors or the current page itself.
Override ``pages/dynamic_tree_menu.html`` if you want to change the
design.
:param page: the current page
:param url: not used anymore
"""
lang = context.get('lang', pages_settings.PAGE_DEFAULT_LANGUAGE)
page = get_page_from_string_or_id(page, lang)
children = None
if page and 'current_page' in context:
current_page = context['current_page']
# if this node is expanded, we also have to render its children
# a node is expanded if it is the current node or one of its ancestors
if(page.tree_id == current_page.tree_id and
page.lft <= current_page.lft and
page.rght >= current_page.rght):
children = page.get_children_for_frontend()
context.update({'children': children, 'page': page})
return context
pages_dynamic_tree_menu = register.inclusion_tag(
'pages/dynamic_tree_menu.html',
takes_context=True
)(pages_dynamic_tree_menu)
它的重点是设置page 和children 变量。
神奇之处在于if 语句,它确保子页面仅在当前页面时显示(current_page 模板变量由呈现django-page-cms 视图的django-page-cms 视图设置,该视图呈现Page),或者如果页面是祖先。 lft & rght 属性由django-mptt 提供,该MPTT 的两个属性是父树节点的左值将小于其子节点的左值并且父节点的正确值将大于其子节点的正确值,您可以通过查看文章Trees in SQL的下图来直观地验证这一点:
Page 的 get_children_for_frontend() 方法调用 MPTTModel.getChildren() 并进行一些过滤以隐藏未发布的 Page 对象,因此没有什么特别之处。
然后该标签使用pages/dynamic_tree_menu.html 模板执行{% include %} 的tag equivalent,然后再次调用pages_dynamic_tree_menu 标签,导致整个事情递归:
{% load pages_tags cache %}
{% if page.calculated_status %}
<li {% ifequal page current_page %}class="selected"{% endifequal %}>
<a href="{% show_absolute_url page %}">{% show_content page "title" %}</a>
{% if children %}
<ul>{% for child in children %}{% pages_dynamic_tree_menu child url %}{% endfor %}</ul>
{% endif %}
</li>
{% endif %}
因此,如果您定义了类似的设置(为您的模板提供顶级部分和当前部分,为该标签定义一个“包含”模板标签和匹配模板)并应用一些样式,那么您应该能够使用它实现目标的方法。