【问题标题】:I'm trying to build site navigation using django-mptt我正在尝试使用 django-mptt 构建站点导航
【发布时间】:2012-10-10 21:48:19
【问题描述】:

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

[Home]
 [News]
    [Politics][Crime][Policing]  etc 
 then [News] > [Politics]
                 [UKIP][Labour] etc

我相信你明白了。 我已经创建了 Section 模型

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

并将 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>

我可以显示所有节点。我想要做的是,基于当前的 url 即 /section/News 获取当前级别的所有兄弟姐妹 上一级的所有祖先和下一级的所有相关后代。所以如果我选择新闻我会得到

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

[Politics][Crime][Education] <- Children on Level 1

我该怎么做?任何帮助将非常感激。我正在学习 Django,所以如果很明显我提前道歉。

【问题讨论】:

    标签: django django-mptt


    【解决方案1】:

    django-page-cms 已经使用django-mptt 解决了这类问题,所以我建议看看那里的处理方式。

    具体来说,template to render the tree menu 是通过称为 pages_dynamic_tree_menucustom 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)
    

    它的重点是设置pagechildren 变量。

    神奇之处在于if 语句,它确保子页面仅在当前页面时显示(current_page 模板变量由呈现django-page-cms 视图的django-page-cms 视图设置,该视图呈现Page),或者如果页面是祖先。 lft & rght 属性由django-mptt 提供,该MPTT 的两个属性是父树节点的左值将小于其子节点的左值并且父节点的正确值将大于其子节点的正确值,您可以通过查看文章Trees in SQL的下图来直观地验证这一点:

    Pageget_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 %}
    

    因此,如果您定义了类似的设置(为您的模板提供顶级部分和当前部分,为该标签定义一个“包含”模板标签和匹配模板)并应用一些样式,那么您应该能够使用它实现目标的方法。

    【讨论】:

    • @Casper 。感谢 Casper,这是一个非常清晰易懂的解释。我现在正在工作,今晚回家后我会试试看,然后回复你。 + 1
    • 不客气。如果这有效或有帮助,如果您接受此答案,我将不胜感激(单击我的答案旁边的勾号轮廓)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-13
    • 1970-01-01
    • 2010-10-05
    • 1970-01-01
    • 2023-03-14
    • 2019-04-16
    相关资源
    最近更新 更多