我正在尝试使用django-mptt构建网站导航

Bil*_*ill 3 django django-mptt

我已经使用django-mptt对我要存储我的文章的标题的部分和子部分等进行建模.有些部分和部分会有孩子,有些则不会.例如:

[Home]
 [News]
    [Politics][Crime][Policing]  etc 
 then [News] > [Politics]
                 [UKIP][Labour] etc
Run Code Online (Sandbox Code Playgroud)

我相信你明白了.我创建了Section模型

class Section(MPTTModel):
    name = models.CharField(max_length=50)
    parent = TreeForeignKey('self',null=True, blank=True, related_name='sub_sections')
    tree = SectionTreeManager()
Run Code Online (Sandbox Code Playgroud)

并通过将Section.objects.all()传递到模板中: -

<ul class="headernav" >

    {% recursetree sections %}

        <li>
            <a href="/section/{{node.name}}" >{{ node.name }}</a>

             {% if node.is_leaf_node %}
                <ul  class="headernav"> 
                    {{ children }}
                </ul>
            {% endif %}
           </li>
    {% endrecursetree %}
</ul>
Run Code Online (Sandbox Code Playgroud)

我可以显示所有节点.我想要做的是,根据当前的网址,即/ section/News,获得当前级别的所有兄弟姐妹,以及以下级别的所有祖先和所有相关的后代.所以,如果我选择了新闻,我会得到

[home][**News**][World][Sports][Culture] <- Level 0

[Politics][Crime][Education] <- Children on Level 1
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?任何帮助将非常感激.我正在学习Django,所以如果很明显我会事先道歉.

Cas*_*par 6

django-page-cms使用了解决这类问题django-mptt,所以我建议看一下它在那里完成的方式.

具体来说,呈现树形菜单模板通过递归包含自定义模板标签pages_dynamic_tree_menu.

这听起来很复杂(自定义ta-whaaa!),所以我会把它分解一下.

我们可以考虑Pagedjango-page-cms是大致相同的Section类.

在99%的网页之间共享的基本模板中,django-page-cms 您可以插入以下代码段:

<ul>
{% for page in pages_navigation %}
    {% pages_dynamic_tree_menu page %}
{% endfor %}
</ul>
Run Code Online (Sandbox Code Playgroud)

pages_navigation 通过设定一个上下文处理器(的方式将项目添加到 模板的上下文中一个Django项目的所有视图),包含顶级网页(即那些没有父母).

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)
Run Code Online (Sandbox Code Playgroud)

它的重点是设置 pagechildren变量.

神奇的是在if语句中,它确保仅在页面是当前页面时显示子项(current_page模板变量由django-page-cms呈现a 的视图设置Page),或者页面是祖先.在lftrght属性提供django-mptt,两本MPTT的属性是父树节点的左值将小于其子节点的左值父节点的权值会比右边的值更它的子节点,您可以通过查看SQL中的树文章中的下图来进行可视化验证:

MPTT的图片

get_children_for_frontend()方法Page的调用MPTTModel.getChildren()与一些过滤隐藏未发布的Page对象,所以没有什么特别的存在.

然后标签做了标记等同{% include %}pages/dynamic_tree_menu.html模板,这反过来又调用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 %}
Run Code Online (Sandbox Code Playgroud)

因此,如果您定义类似的设置(为模板提供顶级部分和当前部分,定义"包含"模板标记和标记的匹配模板)并应用一些样式,那么您应该能够使用此方法来完成你的目标.