Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cannot add arguments to menus. #145

Open
tirohia opened this issue Apr 2, 2024 · 2 comments
Open

cannot add arguments to menus. #145

tirohia opened this issue Apr 2, 2024 · 2 comments
Labels

Comments

@tirohia
Copy link

tirohia commented Apr 2, 2024

Took me a bit, but I can create menus alright. Cannot for the life of me figure out how to dynamically allocate URLs to each of the menus/submenus. I have a list of cancerTypes that I have in the Django database. I am trying to use those to generate a series of sub-menus, one for each cancerType. I can create the menu entries but not link them to a page with a URL using reverse().

My menus.py looks like this:

from django.urls import reverse
from simple_menu import Menu, MenuItem
from hc.models import CancerType
from hc.views import CancerView, SampleView

level1Cancertypes = CancerType.objects.filter(parent__name="ZERO2")
level1 = []

for cancer_type in level1Cancertypes:
    menu_item = MenuItem(
        title=cancer_type.name,  # Use the name of the CancerType as the title of the menu item
        url=reverse("hc:cancer")
    )
    level1.append(menu_item)

Menu.add_item("main", MenuItem(title="All",
                               url=reverse("hc:cancer"),
                               weight=10,
                               children=level1
                               ))

Menu.add_item("main", MenuItem(title="Sample",
                               url=reverse("hc:sample"),
                               # url="/wibble",
                               weight=10,
                               ))

my urls.py looks like this:

from django.urls import path
from . import views

app_name = "hc"
urlpatterns = [
    path("", views.IndexView.as_view(), name="index"),
    path('cancer/', views.CancerView.as_view(), name="cancer"),
    path("sample/", views.SampleView.as_view(), name="sample"),
]

And the relevant part of my views.py looks like this:

from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic
from django.views.generic import ListView, TemplateView 
from .models import CancerType, Sample


class IndexView(generic.ListView):
    template_name = "hc/index.html"


class CancerView(TemplateView):
    template_name = 'hc/cancer.html'


class SampleView(TemplateView):
    template_name = 'hc/sample.html'

All of this I've tried to make as identical as possible to the code in the example4 here — urls.py, views.py and menus.py. This all works.

As soon as I try to pass an argument via kwargs, it falls over. Line 23 of menus.py suggests that I should be able to do something like this in my menus.py:

for cancer_type in level1Cancertypes:
    menu_item = MenuItem(
        title=cancer_type.name,
        url=reverse("hc:cancer", kwargs={'cancerId': 1})
    )

as long as I change the url pattern to be:

urlpatterns = [
    path("", views.IndexView.as_view(), name="index"),
    path('cancer/<int:cancerId>', views.CancerView.as_view(), name="cancer"),
    path("sample/", views.SampleView.as_view(), name="sample"),
]

The instant I do that, though, the app goes down with

Reverse for 'cancer' with no arguments not found. 1 pattern(s) tried: ['hc/cancer/(?P<cancerId>[0-9]+)\\Z']

There's nothing in the example views.py to suggest that I should be changing the number of parameters the view needs.

class SubPageView(TemplateView):
    template_name = 'accounts/subpage.html'

And if I manually enter the URL http://127.0.0.1:8000/hc/cancer/1/, I get a 404. I have checked, there is a cancer type with an id of 1.

Issues #70 and (possibly?) #83 might be suggesting that the reverse call in the menu item creation might be a problem due to being resolved too early or some such? Not sure if I'm following those right. Either way, the subclassing solution in #70 doesn't appear to work for me. Takes me right back to

django.urls.exceptions.NoReverseMatch: Reverse for 'cancer' not found. 'cancer' is not a valid view function or pattern name.

which is exceptionally frustrating when best I can tell from the Django documentation, using "name" in

path('cancer/<int:pk>', views.CancerView.as_view(), name="cancer")

should be sufficient to define a view called "cancer". In short, I have no idea what's happening there (day 2 of Django for me, which might have something to do with it)

Anyway. Any thoughts on what I'm doing would be much appreciated.
Thanks
Ben.

@kytta
Copy link
Member

kytta commented Apr 2, 2024

Hey there Ben! First of, I think you've got the wrong package for your task. django-simple-menu takes care of mostly static menus that are defined on compile startup time. In other words, menus should be decoupled from the database state. Even though your menu works now, any new level 1 cancer type (aka any CancerType with "ZERO2" as parent) will require you to restart the app to re-generate the menus.

For dynamic menus, there would be django-treemenus, but it hasn't been updated in a long time. I think your best bet is not to use any app for the menus, but build them in your template and pass the data via a context variable (in your case, via get_context_data) or inject it with a context processor

Having said that, it doesn't seem like that is causing your problem, but rather early resolution. What you could try is to use a callable instead of a direct reverse call, as implemented in #112:

for cancer_type in level1Cancertypes:
    menu_item = MenuItem(
        title=cancer_type.name,
-        url=reverse("hc:cancer", kwargs={'cancerId': 1})
+        url=lambda r: reverse("hc:cancer", kwargs={'cancerId': 1})
    )

@kytta kytta added the question label Apr 2, 2024
@tirohia
Copy link
Author

tirohia commented Apr 9, 2024

Mōrena.

I may or may not have the wrong package - entirely possible, this is my first foray into django. The menu's I'm trying to create aren't dynamic as such - they might conceivably change once every six months or so, I've only got them in the database because I've been following the tutorials on the django site, and that seems to put everything in the database.

That said, I will have data in the database for visualization related to each of the menu categories that will change, though the format across all the categories will be the same, which is why I'm trying to have a single template and generate the content with the categories in the database. I don't know that that's a super clear explanation.

I suspect it was the early resolution. but sadly the lambda call didn't help. I did get around it in the end by adding an if statement in the menu.html template to check if the item.title was in a set of predefined url's and then substituting a predefined url if it was.
i.e. this in menu.html:

 <li class="nav-item">
            {% for cancer in menuItems %}
            {% if item.title == cancer.label %}
                <a class="nav-link {% if item.selected %}active{% endif %}"
                   href="{{ cancer.url }}">
                    {% if item.icon %}
                        <i class="bi bi-{{ item.icon }}"></i>
                    {% endif %}
                    {{ item.title }}
                </a>
            {% endif %}
            {% endfor %}
        </li>

and this in views.py:

for cancerType in level1CancerTypes:
        menuItems.append({"label":cancerType.name, 'url': f'/hc/cancer/?cancerId={cancerType.id}'})

passing menuItems as part of the context. It's probably not the best way to do it, a bit of a bodge, but it worked, I'm still trying to get my head around the context data.

It works for now, and looks and feels good to use, thanks. Now trying to figure out how/if I can do subsubmenus, or seeing if I can get the navigation into sunburst plot or some such to better represent the hierarchy that I'm trying to navigate. I did see the django-treemenus you mentioned, didn't investigate at the time due to it looking like it's not been updated. Might go have another look to see if there's anything I can use there, thanks.

Cheers
Ben.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants