class SupernautMenu(Menu): home = MenuItem(url='/', display_name=_('Home')) artists = MenuItem(display_name=_('Artists')) albums = MenuItem(display_name=_('Albums')) tracks = MenuItem(display_name=_('Tracks')) class Meta: attrs__class = {'fixed-top': True}
class MyMenu(Menu): sub_menu1 = MenuItem(url='foo', sub_menu=dict(bar=MenuItem(), foo=MenuItem(url='baz'))) sub_menu2 = MenuItem(url='foo', sub_menu=dict(bar=MenuItem(), foo=MenuItem(url='baz'))) sub_menu3 = MenuItem(url='bar', sub_menu=dict(bar=MenuItem(), foo=MenuItem(url='baz')))
class FooPage(Page): menu = Menu(sub_menu=dict( root=MenuItem(url='/'), menu_test=MenuItem(), f_a_1=MenuItem(display_name='Example 1: echo submitted data', url="form_example_1/"), f_a_2=MenuItem(display_name='Example 2: create a Foo', url="form_example_2/"), f_a_3=MenuItem(display_name='Example 3: edit a Foo', url="form_example_3/"), f_a_4=MenuItem(display_name='Example 4: custom buttons', url="form_example_4/"), f_a_5=MenuItem(display_name='Example 5: automatic AJAX endpoint', url="form_example_5/"), f_a_k=MenuItem(display_name='Kitchen sink', url="form_kitchen/"), ), )
class Admin(Page): class Meta: iommi_style = 'bootstrap' table_class = Table form_class = Form apps__auth_user__include = True apps__auth_group__include = True parts__messages = Messages() parts__list_auth_user = dict( auto__include=['username', 'email', 'first_name', 'last_name', 'is_staff', 'is_active', 'is_superuser'], columns=dict( username__filter__freetext=True, email__filter__freetext=True, first_name__filter__freetext=True, last_name__filter__freetext=True, is_staff__filter__include=True, is_active__filter__include=True, is_superuser__filter__include=True, ), ) table_class: Type[Table] = Refinable() form_class: Type[Form] = Refinable() apps: Namespace = Refinable() # Global configuration on apps level menu = Menu( sub_menu=dict( root=MenuItem(url=lambda admin, **_: reverse(admin.__class__.all_models), display_name=gettext('iommi administration')), change_password=MenuItem(url=lambda **_: reverse(Auth.change_password), display_name=gettext('Change password')), logout=MenuItem(url=lambda **_: reverse(Auth.logout), display_name=gettext('Logout')), ), ) @read_config @reinvokable @dispatch( apps=EMPTY, parts=EMPTY, ) def __init__(self, parts, apps, **kwargs): # Validate apps params for k in apps.keys(): assert k in joined_app_name_and_model, f'{k} is not a valid app/model key.\n\nValid keys:\n ' + '\n '.join(joined_app_name_and_model) def should_throw_away(k, v): if isinstance(v, Namespace) and 'call_target' in v: return False if k == 'all_models': return True prefix_blacklist = [ 'list_', 'delete_', 'create_', 'edit_', ] for prefix in prefix_blacklist: if k.startswith(prefix): return True return False parts = { # Arguments that are not for us needs to be thrown on the ground k: None if should_throw_away(k, v) else v for k, v in items(parts) } super(Admin, self).__init__(parts=parts, apps=apps, **kwargs) @staticmethod def has_permission(request, operation, model=None, instance=None): return request.user.is_staff def own_evaluate_parameters(self): return dict(admin=self, **super(Admin, self).own_evaluate_parameters()) @classmethod @class_shortcut( table=EMPTY, table__call_target__attribute='div', ) @require_login def all_models(cls, request, table, call_target=None, **kwargs): if not cls.has_permission(request, operation='all_models'): raise Http404() def rows(admin, **_): for app_name, models in items(django_apps.all_models): has_yielded_header = False for model_name, model in sorted(items(models), key=lambda x: x[1]._meta.verbose_name_plural): if not admin.apps.get(f'{app_name}_{model_name}', {}).get('include', False): continue if not has_yielded_header: yield Struct( name=app_verbose_name_by_label[app_name], verbose_app_name=app_verbose_name_by_label[app_name], url=None, format=lambda row, table, **_: Header(row.name, _name='invalid_name').bind(parent=table).__html__() ) has_yielded_header = True yield Struct( verbose_app_name=app_verbose_name_by_label[app_name], app_name=app_name, name=model._meta.verbose_name_plural.capitalize(), url='%s/%s/' % (app_name, model_name), format=lambda row, **_: row.name, ) table = setdefaults_path( Namespace(), table, title=gettext('All models'), call_target__cls=cls.get_meta().table_class, sortable=False, rows=rows, header__template=None, page_size=None, columns__name=dict( cell__url=lambda row, **_: row.url, display_name='', cell__format=lambda row, **kwargs: row.format(row=row, **kwargs), ), ) return call_target( parts__all_models=table, **kwargs ) @classmethod @class_shortcut( table=EMPTY, ) @require_login def list(cls, request, app_name, model_name, table, call_target=None, **kwargs): model = django_apps.all_models[app_name][model_name] if not cls.has_permission(request, operation='list', model=model): raise Http404() table = setdefaults_path( Namespace(), table, call_target__cls=cls.get_meta().table_class, auto__model=model, columns=dict( select__include=True, edit=dict( call_target__attribute='edit', after=0, cell__url=lambda row, **_: '%s/edit/' % row.pk, ), delete=dict( call_target__attribute='delete', after=LAST, cell__url=lambda row, **_: '%s/delete/' % row.pk, ), ), actions=dict( create=dict( display_name=gettext('Create %(model_name)s') % dict(model_name=model._meta.verbose_name), attrs__href='create/', ), ), query_from_indexes=True, bulk__actions__delete__include=True, ) return call_target( parts__header__children__link__attrs__href='../..', **{f'parts__list_{app_name}_{model_name}': table}, **kwargs, ) @classmethod @class_shortcut( form=EMPTY, ) @require_login def crud(cls, request, operation, form, app_name, model_name, pk=None, call_target=None, **kwargs): model = django_apps.all_models[app_name][model_name] instance = model.objects.get(pk=pk) if pk is not None else None if not cls.has_permission(request, operation=operation, model=model, instance=instance): raise Http404() def on_save(form, instance, **_): message = f'{form.model._meta.verbose_name.capitalize()} {instance} was ' + ('created' if form.extra.is_create else 'updated') messages.add_message(request, messages.INFO, message, fail_silently=True) def on_delete(form, instance, **_): message = f'{form.model._meta.verbose_name.capitalize()} {instance} was deleted' messages.add_message(request, messages.INFO, message, fail_silently=True) form = setdefaults_path( Namespace(), form, call_target__cls=cls.get_meta().form_class, auto__instance=instance, auto__model=model, call_target__attribute=operation, extra__on_save=on_save, extra__on_delete=on_delete, ) return call_target( **{f'parts__{operation}_{app_name}_{model_name}': form}, **kwargs, ) @classmethod @class_shortcut( call_target__attribute='crud', operation='create', parts__header__children__link__attrs__href='../../..', ) def create(cls, request, call_target, **kwargs): return call_target(request=request, **kwargs) @classmethod @class_shortcut( call_target__attribute='crud', operation='edit', parts__header__children__link__attrs__href='../../../..', ) def edit(cls, request, call_target, **kwargs): return call_target(request=request, **kwargs) @classmethod @class_shortcut( call_target__attribute='crud', operation='delete', parts__header__children__link__attrs__href='../../../..', ) def delete(cls, request, call_target, **kwargs): return call_target(request=request, **kwargs) @classmethod def urls(cls): return Struct( urlpatterns=[ path('', cls.all_models), path('<app_name>/<model_name>/', cls.list), path('<app_name>/<model_name>/create/', cls.create), path('<app_name>/<model_name>/<int:pk>/edit/', cls.edit), path('<app_name>/<model_name>/<int:pk>/delete/', cls.delete), ] + Auth.urls().urlpatterns )
class MyMenu(Menu): sub_menu1 = MenuItem(url='foo', sub_menu=dict(bar=MenuItem(), external=MenuItem(url='http://example.com'), foo=MenuItem(url='baz'))) sub_menu2 = MenuItem(url='foo', sub_menu=dict(bar=MenuItem(), external=MenuItem(url='http://example.com'), foo=MenuItem(url='baz'))) sub_menu3 = MenuItem(url='bar', sub_menu=dict(bar=MenuItem(), external=MenuItem(url='http://example.com'), foo=MenuItem(url='baz'))) external = MenuItem(url='http://example.com')
def test_template(): menu = Menu( template=Template('{{ menu.sub_menu.foo.display_name }}'), sub_menu=dict(foo=MenuItem()) ).bind(request=req('get')) assert menu.__html__() == 'Foo'
class MyMenu(Menu): sub_menu = MenuItem(url=None, sub_menu=dict(bar=MenuItem(), foo=MenuItem(after=0)))
class MyMenu(Menu): home = MenuItem(url='/') artists = MenuItem() albums = MenuItem() external = MenuItem(url='http://example.com') songs = MenuItem()
class MyMenu(Menu): home = MenuItem(url='/') artists = MenuItem() albums = MenuItem(include=False) empty_sub_menu = MenuItem(url=None, sub_menu=dict(foo=MenuItem(include=False)))
class MyMenu(Menu): foo = MenuItem(url=reverse_lazy('reverse-lazy-test'))
class MyPage(Page): menu = Menu(sub_menu=dict(root=MenuItem()))