Peter
Peter

Reputation: 1669

DjangoCMS NavigationNode - custom node

The default NavigationNode in DjangoCMS has nodes as:

In the same context, I want to add my custom node, called: category_name which would return a string 'something' (just for testing purposes).

Following the official documentation, nothing that would help me solve my issue, was found. When looking up for the declaration of default NavigationNode, the following code appears:

from menus.base import NavigationNode

base.py file:

class NavigationNode(object):

    def __init__(self, title, url, id, parent_id=None, parent_namespace=None,
                 attr=None, visible=True):
        self.children = []  # do not touch
        self.parent = None  # do not touch, code depends on this
        self.namespace = None  # TODO: Assert why we need this and above
        self.title = title
        self.url = url
        self.id = id
        self.parent_id = parent_id
        self.parent_namespace = parent_namespace
        self.visible = visible
        self.attr = attr or {} # To avoid declaring a dict in defaults...

    def __repr__(self):
        return "<Navigation Node: %s>" % smart_str(self.title)

    def get_menu_title(self):
        return self.title

    def get_absolute_url(self):
        return self.url

    def get_attribute(self, name):
        return self.attr.get(name, None)

    def get_descendants(self):
        return sum(([node] + node.get_descendants() for node in self.children), [])

    def get_ancestors(self):
        if getattr(self, 'parent', None):
            return [self.parent] + self.parent.get_ancestors()
        else:
            return []

If I add my custom node called category_name here, it will work. But it is not a smart solution to modify base files.

So my question is:

How to add my custom node outside the base.py ? i.E. if I want to add it in my apps models.py file. Is this even possible?

Upvotes: 0

Views: 355

Answers (1)

Peter
Peter

Reputation: 1669

I finally found the solution. The answer is: Yes! It is possible.

As it states in the documentation, you can use modify() method.

But it did not work for me, because I got confused with node.attr["changed_by"] . In templates, I wanted to use something like this: {{ child.category_name }} but as it is obvious, I was modifying it wrong.

The correct way is this:

from menus.base import Modifier
from menus.menu_pool import menu_pool

from cms.models import Page

class MyMode(Modifier):
    """

    """
    def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb):
        # if the menu is not yet cut, don't do anything
        if post_cut:
            return nodes
        # otherwise loop over the nodes
        for node in nodes:
            # does this node represent a Page?
            if node.attr["is_page"]:
                # if so, put its changed_by attribute on the node
                node.category_name = "Some category name here"
        return nodes

menu_pool.register_modifier(MyMode)

Now, in menu.html, you can use child.category_name and it will output the string "Some category name here"

{% load i18n menu_tags cache %}

{% for child in children %}
<li>
    {% if child.children %}
    <a href="{{ child.get_absolute_url }}">
        {{ child.get_menu_title }} <span class="caret"></span>
    </a>
    <ul class="menu-vertical">
        {% show_menu from_level to_level extra_inactive extra_active template "" "" child %}
    </ul>
    {% else %}
        <a class1="{{ child.get_absolute_url }}" href="{{ child.get_absolute_url }}">
            {% if child.category_name %}
                <b>{{ child.category }}</b>
            {% endif %}
            {{ child.get_menu_title }}
        </a>
    {% endif %}
</li>
{% if class and forloop.last and not forloop.parentloop %}{% endif %}
{% endfor %}

Finally, after hours and hours of attempts, I solved this.

Upvotes: 1

Related Questions