Search code examples
djangodjango-cms

django-cms redirect top level menu to first child


I would like to redirect the top menu items in a page I'm writing to point at their children if they have any. E.g. this site has a top level About, with several CMS pages underneath. When a user clicks on about, I want to show the first child page under the top-level about menu.

I can do it like the below, but it feels wrong. I know I can do a static redirect, but I don't want to do that, as the first child might change (it's a CMS system after all) and I don't want the people entering data into the CMS to have to manage a redirect. So I prefer the below to a configured redirect.

This is a pretty common requirement, is there a standard way of doing this? I think I might be able to do it with a navigation modifier, so I might try that route next.

    <div style="display:none;">{% show_menu 0 1 1 %}</div>
    <div id="topmenu">
      <ul>
      {% for child in children %}
          <li>
          {% if child.children %}
          <a href="{{child.children.0.get_absolute_url}}">
          {% else %}
          <a href="{{child.get_absolute_url}}">
          {% endif %}
          {{ child.get_menu_title }}
          </a>
          </li>
      {% endfor %}
      </ul>
    </div>

About the above, I need to have the show_menu in there otherwise the data isn't there for my custom menu. Is there any way for this not to be needed?

The thing I dislike the most about the above, is that the /about/ page is still active. It's not the worst thing in the world if someone browses there, they'll get an empty page, but I'd rather that not happen. I think if I wrote a navigation extension the same disadvantage would be there. I guess I need a programmatic redirect to redirect to the first child if that page is hit. Thoughts? I can probably do it in template logic, but that seems crazy.


Solution

  • 2018: updated version, that works in cms 3.4+ and new middleware style compatible available here. Once you added the /firstchild, you'll need to edit/publish the redirecting page in the cms pages's change list, as, you guess it, will redirect.


    solution with middleware and custom redirect field value of "/firstchild", to trigger redirects.

    • plus: you won't have any blank pages
    • plus: it's flexible, as it allows pages that will not be redirected to subpages
    • plus: no action needed when the first child's url changes
    • minor drawback: there is always a redirect ( 301, though, not that bad ;)
    • minor drawback: configuration is a bit nerdy, as you will have to enter "/firstchild" into the "redirect" field. definitely not end-user friendly, but works. the cms itself doesnt provide a better solution at all (not even in 2018).

    from django.shortcuts import redirect
    
    # redirect to first subpage if the "redirect" field is exactly "_subpage"
    class RedirectSubpageMiddleware(object):
        def process_view(self, request, view_func, view_args, view_kwargs):
            if request.current_page:
                page = request.current_page
                if "slug" in view_kwargs and page.get_redirect() == "/firstchild":
                    subpages = request.current_page.children.all()
                    if subpages.count():
                        # can redirect! must make uri!
                        return redirect(subpages[0].get_absolute_url(), permanent=True)
            return None