I’m working on a project and I need to be able to programatically inject a number of application-model-based items into an existing CMS-based menu. I’ve started to use the code found here to modify the menu: http://docs.django-cms.org/en/2.3.3/extending_cms/app_integration.html.
EDIT: Much more detail
My client would like me to attach application-model objects to the CMS menu so that they are children of an existing CMS page in the menu. I currently have a total hack in place which requires me to make fake pages in the CMS that are children of the desired menu item, have the same name as the application-model objects, then, I’ve installed a Modifier like so:
class SportsMenuModifier(Modifier):
def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb):
if post_cut:
return nodes
for node in nodes:
if node.title == "Baseball":
node.url = "/sports/baseball"
elif node.title == "Football":
node.url = "/sports/Football"
elif node.title == "Bowling":
node.url = "/sports/bowling"
elif node.title == "Golf":
node.url = "/sports/golf"
return nodes
menu_pool.register_modifier(SportsMenuModifier)
There is so much wrong with this I don’t know where to begin, but I’ll use this non-exhaustive list to highlight some of the most basic issues:
- Requires the presence of “Fake” CMS pages
- Ridiculously dependent on the naming of the application sports objects and the fake pages
- Will
not detect when the customer creates new sports objects - Very confusing for everyone involved
What I was hoping I could do was something like this (WILL NOT WORK):
in models.py
from django.db import models
from cms.models.pagemodel import Page
class Sport(models.Model):
name = models.CharField(...)
parent = modes.ForeignKey(Page, ...)
...
in menu.py
class SportsMenu(Menu):
def get_nodes(self, request):
nodes = []
for sport in Sports.objects.filter(...).order_by('order'):
node = NavigationNode(
_(sport.name),
sport.get_absolute_url(),
sport.pk,
sport.parent.pk
)
nodes.append(node)
return nodes
menu_pool.register_menu(SportsMenu)
I’m not sure I can continue with this approach because:
-
I don’t know how to fetch the menu.namespace for a given Page
-
Even when I hard-code the namespace to “CMSMenu” (I read somewhere this is what it is), this still does nothing that I can see.
So, how do we attach application-model based objects as children to existing CMS-page-based menu items?
The answer for this is that I should have been using (and AM using) Attach Menus which are, unfortunately, VERY poorly documented here: https://django-cms.readthedocs.org/en/latest/extending_cms/app_integration.html#attach-menus.
Also, while I was following those instructions, I accidentally imported CMSAttachMenu from menus.base rather than from cms.menu_bases which doesn’t result in any errors, but also doesn’t do anything, so, it was rather difficult to debug =/
Here is some working code in case it helps anyone in the future:
in models.py
In menu.py
Once these two files are in-place, restart the service. In Django-CMS, navigate to the page whose menu-item you’d like to have the various Sports object appear as children menu-items in your menu.
In the Advanced Settings section (which is normally collapsed), you’ll see a new option “Attached Menu”, choose the new item “Sports Sub-Menu” and you’ll be in business.