jazzband / django-simple-menu Goto Github PK
View Code? Open in Web Editor NEWSimple, yet powerful, code-based menus for Django applications
License: BSD 2-Clause "Simplified" License
Simple, yet powerful, code-based menus for Django applications
License: BSD 2-Clause "Simplified" License
All tarballs from pypi i tested are containing
Menu processing is not thread-safe.
See also #27
Hi,
I had used simple-menu with satisfaction a couple of years ago. In a new project, it appeared not to work anymore.
After tweaking a few hours, I realized that the only difference in my code was the use of the extends tag in the template.
If your template extends another one, you have to move to the latter the tags {% load menu %} and {% generate_menu %}; otherways, the menus context variable is not defined.
But I think that not having to modify directly a generic template is just why extends exist.
Anyway, many thanks to the creator of the django-simple-menu app! Really it allows to create "Simple, yet powerful, code-based menus for Django applications".
Despite I shouldn't write this ;-), I think it may be of help in django-simple-menu.
I've tried many menu systems, and all are flawed with issues that render them unusable for bigger projects. DSM ist great, really, just lacks some features that are urgently needed in projects with "special needs", like those using django-hosts.
When using django-hosts, using dynamic hosts (stored in database and checked vie middleware against current domain) with django-simple-menu, you run into a problem that is not easy to solve. When you e.g. have a host, whose urls.py has no /admin/
path, and another host, which has path(/admin/
,...) AND a MenuItem with a url named reverse(admin:index)
, you get an Exception when opening the first host's site: as generate_menu
runs through ALL MenuItems (of all hosts), but the urls.py doesn't contain a /admin
path, the reverse(admin:index)
points to a non-existing URL. Bang.
I struggled many days with this issue, and finally came up with a solution: using reverse_lazy
instead of reverse
and subclassing MenuItem + adding code to get the current View's name, I can determine which menu items are visible on that domain.
This solution is not specific to django-hosts - in fact - it has nothing to do with hosts at all, it is generic: If you add view_name
as property to MenuItem, and add some logic that it is invisible when the current view is not the one specified in view_name
, everything is fine.
So I would propose a newattr to MenuItem: view_name
:
def __init__(self, ..., view_name="",...):
#...
self.view_name = view_name
#...
def process(self, request: HttpRequest):
if self.view_name:
self.visible = request.resolver_match.view_name == self.view_name
if not self.visible:
return
super().process(request)
Please tell me what you think about that.
Hello!
I have menu which based on my models. One model have 'is_active' option which is boolean. When I'm set it to False it's should be exclude from menu, but it's happens only when I'm restart server.
Here is part of code:
`for single_league in leagues:
menu_items = []
teams = Team.objects.filter(league=single_league.pk, team_is_active=True)
for team in teams:
item = MenuItem(team.team_name, reverse('team', kwargs={'pk': team.pk}), separator=True)
menu_items.append(item)
if len(menu_items) > 0:
# show menu only if League have teams
Menu.add_item("main", MenuItem(single_league.name.upper(),
reverse("teams", kwargs={'pk': single_league.pk}),
weight=30, children=tuple(menu_items)))
else:
Menu.add_item("main", MenuItem(single_league.name.upper(),
reverse("teams", kwargs={'pk': single_league.pk}), weight=30))`
As you see here teams = Team.objects.filter(league=single_league.pk, team_is_active=True)
I'm loading only active items. So maybe I missed something? Or maybe when I'm update model object I need do something?
Have some trouble using the template tag do render the menu:
'cmp' is an invalid keyword argument for this function
seems like cmp is not more valid
If you install django-simple-menu (here on uberspace / centos 7), there is an error. Under 3.9 you get a warning about "setup.py install":
Using legacy 'setup.py install' for django-simple-menu, since package 'wheel' is not installed.
being used as legacy method. But under 3.10 it fails:
pip install django-simple-menu --user
Collecting django-simple-menu
Using cached django-simple-menu-1.2.1.tar.gz (15 kB)
Preparing metadata (setup.py) ... error
error: subprocess-exited-with-error
× python setup.py egg_info did not run successfully.
│ exit code: 1
╰─> [10 lines of output]
/usr/lib/python3.10/site-packages/setuptools/_distutils/dist.py:257: UserWarning: Unknown distribution option: 'include_package_data'
warnings.warn(msg)
/usr/lib/python3.10/site-packages/setuptools/_distutils/dist.py:257: UserWarning: Unknown distribution option: 'install_requires'
warnings.warn(msg)
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
or: setup.py --help [cmd1 cmd2 ...]
or: setup.py --help-commands
or: setup.py cmd --help
error: invalid command 'egg_info'
[end of output]
note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed
× Encountered error while generating package metadata.
╰─> See above for output.
#Have trouble while using add some additional data for my url using python string format,
so
url='{}?test=a&test_test=b'.format(reverse_lazy('app:urlname')),
That's why i don't pass match_url
. exact_url=True
dosen`t help.
Django 4.1 has been out for a while now. Since the backwards incompatible changes concern mostly the database backend, django-simple-menu
should work as-is with no modifications.
Here's the code I had to use to make it work. What do you think about incorporating something of sorts in the module?
class MenuItemTranslated(MenuItem):
def __init__(self, name, url, reverse_kwargs=None, check=None, **kwargs):
MenuItem.__init__(self, name, '', check=None, **kwargs)
self.check = check
self.url_ = url
self.reverse_kwargs = reverse_kwargs
def process(self, request):
url = reverse(self.url_, kwargs=self.reverse_kwargs)
self.url = url
if callable(self.check):
self.visible = self.check(request)```
For some reason, the following menus don't seem to be displayed properly. When the app is loaded (and no user is logged in), the "Log In" link is shown and the "Log Out" link is not shown (correctly).
When the user is logged in, neither link is shown. (Originally, I blamed it on django-auth-ldap, but that doesn't seem to be the case, as there's no set of truth values which could cause this behavior.)
However, when I kill the server and restart it, Log Out is shown correctly. If I then Log Out, then neither link is shown (again). If I kill the server (again), then the Log In button is shown correctly.
I've tried both the 'master' branch from github and the latest verison in pip.
This could somehow still be either my fault or django-auth-ldap's fault, but it seems like the issue is here.
Thanks for looking over this report.
menus.py
from django.core.urlresolvers import reverse
from menu import Menu, MenuItem
children = (MenuItem("Log In",
reverse("auth.views.login"),
check=lambda request: request.user == None or (not request.user.is_authenticated())),
MenuItem("Log Out",
reverse("auth.views.logout"),
check=lambda request: request.user != None and request.user.is_authenticated()),
)
Menu.add_item("main", MenuItem("Auth", reverse("auth.views.home"), children=children))
Our actions currently spawn for every combination of Python and Django. Some of these run empty, since we don't have the tox envs for them. Instead, I would just leave the Python versions in the matrix and do the testing with tox. This way, we will spawn way less runners, which will result in our CI running faster.
Another idea: Upload coverage once, in a different step. Would also quicken our CI runs.
Hi,
after update from version 1.1.0 to 1.1.1 I got issue:
'tuple' object has no attribute 'sort'
How I have to change menu configuration. And please change documentation. I think is the same like in version 1.1.0.
THX
Hello again.
Wouldn't be better to rename name of module to something a bit closer to package name ? Couldn't it be named "django_simple_menu" ?
Anyway, glad I found this package as this is closest to my implementation of menu in PHP where I came from :-) And in my new Django days I'd like to use existing packages as much as possible and avoid reinventing the wheel.
Menu.add_item("sidebar", MenuItem(
"Tasks",
reverse("project:task:list", args=[project.id]),
weight=2,
check=lambda request: request.user.has_perm('task.can_access'),
icon="clipboard",),
)
how to get the project id in the above code??
Hi guys,
I am trying to create submenu and I did create children but I don't know how to display them on a webpage. I tried all different things using has_submenu, and try to access submenu etc. Can someone help?
I want to do something like
HOME | PRODUCTS | SOLUTIONS | ABOUT US
proudct-1 solution-A
product-2 solution-A.1
prd-2.1 solution-B
Can you add an option to sort the children of a MenuItem based on the child's weight?
Regards
Hi there!
I discovered a problem in my project which involves 3 levels of menus with MENU_SELECT_PARENTS being set. So a structure like this:
Menu.add_item("main", MenuItem("A", '/a',
children=(MenuItem("B", '/b',
children=(MenuItem("C", '/') ,)),)))
and the current page being '/', so C will be selected, the simple menu does not set MenuItem B to selected (A and C are selected though).
I did not dig deep into the code, but I believe that setting child.selected = True
in the is_child_selected
function, the menu works correctly again. So:
if getattr(settings, 'MENU_SELECT_PARENTS', False):
def is_child_selected(item):
for child in item.children:
if child.selected or is_child_selected(child):
child.selected = True
return True
fixes the issue in my case.
the docs say that {% generate_menu %}
needs to be called within a block. I think this is not correct, as it seems to need to be called within the same block you are using the menu.
Using a simple pattern to demonstrate this:
# base.html template
<html>
<head>
</head>
<body>
{% block generate_menu %}
{% generate_menu %}
{% endblock %}
{% block content %}
Empty page.
{% endblock %}
</body>
</html>
Now use a template that inherits this:
# application.html
{% extends 'base.html' %}
{% block content %}
Now you should see menus, but you don't.
{{ menus }}
{% endblock %}
Here you just override the content
block. The generate_menu
block should be inherited by extend
and be untouched, so generate_menu
should be called - which I can confirm by debugging.
The problem seems to be that the context which is passed to MenuNode.render()
does't seem to get passed through to the correct template in application.html.
If I add {% generate_menu %}
within the content
block in application.html
, everything is ok. Even if I add {% generate_menu %}
in a new generate_menu
block in application.html
(which is "a block", like the docs say), it does NOT work.
Is this intended behaviour? Or a bug?
If the environment is set up in virtualenv and packaging is controlled with buildout then automatic setup fails with:
ImportError: No module named django.conf
This is caused by the fact that setup.py includes init.py which imports django.conf which is not available during buildout.
My solution was to hardcode version info to setup.py.
I found a problem i have many menus and many applicattions.
I have one project with 4 app, each app have its own menus.py. and have two kind of menu "main" and "settings".
In staff/menus.py i have
myaccount_children = (
MenuItem("Add Employee",
reverse("staff-create"),
weight=10),
MenuItem("View",
reverse("staff"),
weight=80),
)
Menu.add_item("main", MenuItem("Staff",
reverse("staff"),
weight=10,
icon="group",children=myaccount_children))
Menu.add_item("settings", MenuItem("Manager",reverse("manager")))
and in inventory/menus.py
myaccount_children = (
MenuItem("Check Availability",''),
MenuItem("Add Equipment",''),
MenuItem("Stock Control",''),
MenuItem("Assign Equipment",''),
MenuItem("Return Equipment",''),
)
Menu.add_item("main", MenuItem("Inventory & Stock Control",
'',
icon="hdd-o",children=myaccount_children))
All work fine until i add:"Menu.add_item("settings", MenuItem("Categories",reverse("categories")))"
to inventory/menus.py , the system throught "ImproperlyConfigured: The included urlconf binventory.urls doesn't have any patterns in it"
however if i add this line to the other file it work perfect.
Right now it seems that there's no way to sublcass MenuItem as it will cause circular import. Any idea how to allow subclassing MenuItem?
With the following "menu" configuration:
produccion_submenus = ( MenuItem("Informar Accidente", reverse("produccion:accidente_crear"), check=lambda request: request.user.groups.filter(name='produccion_admin').exists() ), ) Menu.add_item("main", MenuItem(u"Producción", "#", children=produccion_submenus, check=lambda request: request.user.groups.filter(name='produccion_base').exists() ), )
If the user is assigned to the "produccion_base" group, "Produccion" item and its submenu "Informar Accidente" are shown.
Note: The user is not assigned to the "produccion_admin" group.
Trying your example on ubuntu 16.04 as in the README.rst just brings the following:
./manage.py syncdb
Unknown command: 'syncdb'
Type 'manage.py help' for usage.
Hi,
I've followed the examples pretty closely and am not able to get menus to generate. I've put some logic around the code in order to see where things are failing and it appears that everything gets stuck on this line: {% generate_menu %}
{% load menu %}
{% block content %}
once
two
three
booyah
{% if item.separator %}It'll be very nice if one could hide menu items that have no visible children (e.g. permission check has failed).
As stated in #78, MenuItem's __init__()
adds all additionally added kwargs as instance variables. This is great for new args lico icon
etc., as long as they are "static", meaning they do not change over time in the template's context.
There are many variables I can think of (even icon
!) like
badge
(which should return e.g. a number of new emails), ordisabled
(which should be true if a certain condition is met)where you would need to add it to MenuItem, and it would be easy with kwargs
, but MenuItem doesn't check if it is callable.
So I would propose to
self.kwargs
to have a reference laterdef __init__(..., **kwargs)
# ...
self.kwargs = kwargs
# keep a link of all kwargs in the instance, for convenience/template context
for k in self.kwargs:
setattr(self, k, self.kwargs[k])
def process(self, request):
# ...
# if kwargs are callable, call them and save the results in the actual instance variables
for key, value in self.kwargs:
if callable(value):
setattr(self, k, self.kwargs[k]())
Currently, if a MenuItem
receives a URL with query params, since the url match is done against the request.path
, there is no way for that item to be checked as active.
For example, if I configure this item:
Menu.add_item("main", MenuItem(_("Item"), reverse('app:my_view')+'?status=active', weight=10))
That item will not be marked as the active item. Maybe MenuItem()
could receive an additional argument to be able to take into account the current queryparam for the match_url()
method.
@borgstrom
You could create an example of how to use views that require parameters as a UpdateView
Hello again.
I'm using simple-menu in my current multilingual project. I'd like to use ugettext() for my menu titles as bellow:
from django.utils.translation import ugettext as _
Menu.add_item("main_en",
MenuItem(_(u'Home'),
reverse('coltburg.views.home'),
weight=10,
)
)
Menu.add_item("main_en",
MenuItem(_(u'About'),
reverse('coltburg.views.about'),
weight=20,
)
)
But after running webserver, menu shows correct strings for selected language and than it shows these string no matter what language selected forever. Looks like it caches initial strings for initial language and then does not call ugettext() next time.
So far fixed with ugly workaround, but if possible, would love to implement as my example shows and don't keep multiple menus for every language, but keep translation of strings in .po and .mo files.
When trying to install the requirements for the example (inside /example/
), the pysqlite
install fails with the following error:
> pip install -r requirements.txt
Collecting Django
Using cached Django-4.0.5-py3-none-any.whl (8.0 MB)
Collecting django-simple-menu
Using cached django-simple-menu-1.2.1.tar.gz (15 kB)
Preparing metadata (setup.py) ... done
Collecting pysqlite>=2.6.0
Using cached pysqlite-2.8.3.tar.gz (80 kB)
Preparing metadata (setup.py) ... error
error: subprocess-exited-with-error
× python setup.py egg_info did not run successfully.
│ exit code: 1
╰─> [1 lines of output]
pysqlite is not supported on Python 3. When using Python 3, use the sqlite3 module from the standard library.
[end of output]
It seems like the dependency only supports Python 2 and is no longer maintained. I also couldn't see where it was imported in the project. Shouldn't it be removed?
hi, i want to use target="blank" with simple menu. So how can i do it?
I did read document but it doesn't help.
Could you please update the example code to run under new django 1.10+ requirements?
Due to how Django's template search algorithm works, it is recommended to put templates inside subdirectories which are named after applications. Our bootstrap templates currently are in the root folder, which is not ideal. Instead, put them in a directory named after our app.
After #93 is merged, the path will be simple_menu/templates/simple_menu/bootstrap-navbar*.html
This issue tracks the implementation of the Jazzband guidelines for the project django-simple-menu
It was initiated by @borgstrom who was automatically assigned in addition to the Jazzband roadies.
See the TODO list below for the generally required tasks, but feel free to update it in case the project requires it.
Feel free to ping a Jazzband roadie if you have any question.
README
fileCONTRIBUTING.md
filejazzband
account to PyPI project as maintainer role (URL: https://pypi.python.org/pypi?:action=role_form&package_name=<PROJECTNAME>
)jazzband-bot
as maintainer to the Read the Docs project (URL: https://readthedocs.org/dashboard/<PROJECTNAME>/users/
)Description | Simple, yet powerful, code-based menus for Django applications |
Homepage | None |
Stargazers | 65 |
Open issues | 8 |
Forks | 39 |
Default branch | master |
Is a fork | False |
Has Wiki | False |
Has Pages | False |
The README.md
for django-simple-menu
makes a reference to TEMPLATE_CONTEXT_PROCESSORS
. That section was removed in Django 1.1.
The other problem is that the same release also removed django.core.context_processors module
. The Django 1.8 release notes say that django.template.context_processors.request
is the replacement: "Built-in template context processors have been moved to django.template.context_processors."
As of Django 1.7 the setting mentioned needs to go in:
TEMPLATES = [
{
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
]
}
}
]
menu.py
importing has a catch all that hides all errors making you think the plugin is not working when your code is at fault
https://github.com/borgstrom/django-simple-menu/blob/master/menu/menu.py#L75
My snippet:
try:
__import__(menu_module, fromlist=["menu", ])
except ImportError as err:
traceback.print_tb(err.__traceback__)
pass
There is a bug in multi user environments:
The visibility of an item does not get reset between requests of different users. Therefore a user with a lot of privileges will not see all of it's menu items when a user with less privileges already populated the visible flag of a menu item.
It's not possible to restore the default value of visible since it is overwritten by the check callback.
Is this project still alive? There are 11 pull requests, some >1 year old; I just mane two tiny fixes (a typo and an example correction of a path in settings, which fixes a bug), and even the PRs are not coming through the CI ,because a CI config file is missing or defective...
Is there anyone actively maintaining this package?
related to #19
I struggled also about the issue to catch the current pk
attribute from the current request and pass it into the menu.
I've got the following urls.py
for example:
app_name = 'structure'
urlpatterns = [
...
path('groups', MrMapGroupTableView.as_view(), name='group_overview'),
path('groups/new', NewMrMapGroup.as_view(), name='group_new'),
path('groups/<pk>', MrMapGroupDetailView.as_view(), name='group_details'),
path('groups/<pk>/members', MrMapGroupMembersTableView.as_view(), name='group_members'),
path('groups/<pk>/edit', EditGroupView.as_view(), name='group_edit'),
...
]
How can i resolve the current requested pk
from the request kwargs for example in the menus.py
:
# Define children for the group menu
group_details_children = (
MenuItem("Edit Group",
reverse('structure:group_edit', args=pk), # pk not resolved here for now
weight=10,
icon="user"),
MenuItem(_("Members"),
reverse('structure:group_members', args=pk), # pk not resolved here for now
weight=20,
MenuItem(_("Publishers"),
reverse('structure:group_publishers', args=pk), # pk not resolved here for now
weight=30,
)
# Add a My Account item to our user menu
Menu.add_item("group_details", MenuItem(_("Details"),
reverse('structure:group_details', args=pk), # pk not resolved here for now
weight=10,
children=group_details_children))
IMO, the current name of the Python module (menu
) is too broad. I can imagine people using it for their own Python modules or Django apps (think of a restaurant website with an app for menus).
I propose renaming the Python module to simple_menu
, similarly to how many other Django packages do it.
EDIT: of course, we will need to bump the major version, but we do it anyway with the v2.0.0 release
This Module do not work with Django Version 2.1.2 and Python 3.7.
File "/Users/User/projects/product/venv/lib/python3.7/site-packages/django/template/base.py", line 850, in _resolve_lookup
(bit, current)) # missing attribute
django.template.base.VariableDoesNotExist: Failed lookup for key [blubb] in {}
Exception while resolving variable 'main' in template 'index.html'.
Traceback (most recent call last):
File "/Users/rt1330/projects/iaiHub/venv/lib/python3.7/site-packages/django/template/base.py", line 829, in _resolve_lookup
current = current[bit]
KeyError: 'main'
Great work on this project.
Can I request the ability to add a "divider" to a menu. I intend to use this app with Twitter Bootstrap v3 and currently my manually crafted menus have <li class="divider"></li>
In them to make them a bit easier to read.
It would be good if I could somehow programatically add a divider so when iterating over the menu in a template I can do something like {% if item.is_divider %}<li class="divider"></li>{% endif %}
Hi, in this line https://github.com/fatbox/django-simple-menu/blob/master/menu/menu.py#L54 there is a invalid check of django applications.
Probably something like app.startswith('django.')
avoid a lot of issues.
Currently, using submenu
is quite confusing. It works fine for two menu levels, but there are problems using it with more levels (see also #47).
I wonder, if changing the underlying structure of the tags might help. I imagine a linked-list kind of deal, which itself describes a path in the menu tree from the root to the current active page. This way, one could search and/or iterate the whole way from the root to the page, making working with children AND grandchildren easy.
I will try to jot down some idea of how this should work :)
I'm using the Bootstrap top nav. My top menus are primarily anchor tags (href="#").
It would be great to be have these show as "active" if one of their submenu items is selected.
I suppose it would be controlled by a configurable boolean.
Encountered one issue. If placing {% generate_menu %} tag outside of a block, it will not generate any menus (as render() method is not called ). Ex. this template will not show any menus (and no error shown as well):
{% extends "base.html" %}
{% load menu %}{% generate_menu %}
{% block content %}
...
{% for item in menus.main %}
...
{% endfor %}
{% endblock %}
But his will:
{% extends "base.html" %}
{% load menu %}
{% block content %}
{% generate_menu %}
...
{% for item in menus.main %}
...
{% endfor %}
{% endblock %}
I guess it's worth to mention this in docs, otherwise it maybe confusing for people who try library first time.
It would be useful to pass context to the functions such as title, url and check so that we can extract information from the context as well as the request.
Something like:
check(request, context)
I've just installed django-simple-menu and tried it out.
But the file menus.py doesn't seem to do anything.
It's inside my app-directory (same level as urls.py, views.py etc.), it tried to trigger an error, but it is not registered anywhere:
/app/menus.py
this should fail, but does not:
from menu import Menu, MenuItem
MMMMenu.add_item("main", MenuItem("Events",
reverse("manager.manevents.views.events"),
weight=10))
I'm using Django 1.5, I'm sure the app is installed correctly (with PIP) (and enabled in INSTALLED_APPS) since I can provocate errors from my templates.
But the menus object remains empty. Probably due to that menus.py isn't run.
Is there anything that I can do wrong?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.