3.4-beta1: Plugin object detail views with tabs cause exceptions #7256

Closed
opened 2025-12-29 20:20:56 +01:00 by adam · 0 comments
Owner

Originally created by @peteeckel on GitHub (Nov 18, 2022).

Originally assigned to: @jeremystretch on GitHub.

NetBox version

v3.4-beta1

Python version

3.8

Steps to Reproduce

  1. Install a plugin, e.g. netbox-dns
  2. Open an object view inheriting from generic/object.html, e.g. for a record

Expected Behavior

The object view opens in the GUI, displaying object details

Observed Behavior

A "NoReverseMatch" is generated for the view.

Environment:

Request Method: GET
Request URL: http://192.168.106.105/plugins/netbox-dns/zones/1/

Django Version: 4.1.2
Python Version: 3.8.11
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.humanize',
 'corsheaders',
 'debug_toolbar',
 'graphiql_debug_toolbar',
 'django_filters',
 'django_tables2',
 'django_prometheus',
 'graphene_django',
 'mptt',
 'rest_framework',
 'social_django',
 'taggit',
 'timezone_field',
 'circuits',
 'dcim',
 'ipam',
 'extras',
 'tenancy',
 'users',
 'utilities',
 'virtualization',
 'wireless',
 'django_rq',
 'drf_yasg',
 'netbox_dns.DNSConfig']
Installed Middleware:
['graphiql_debug_toolbar.middleware.DebugToolbarMiddleware',
 'django_prometheus.middleware.PrometheusBeforeMiddleware',
 'corsheaders.middleware.CorsMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'netbox.middleware.ExceptionHandlingMiddleware',
 'netbox.middleware.RemoteUserMiddleware',
 'netbox.middleware.LoginRequiredMiddleware',
 'netbox.middleware.DynamicConfigMiddleware',
 'netbox.middleware.APIVersionMiddleware',
 'netbox.middleware.ObjectChangeMiddleware',
 'django_prometheus.middleware.PrometheusAfterMiddleware']


Template error:
In template /opt/netbox/netbox/templates/generic/object.html, error at line 91
   'netbox_dns' is not a registered namespace
   81 :   <ul class="nav nav-tabs px-3">
   82 :     {# Primary tab #}
   83 :     <li class="nav-item" role="presentation">
   84 :       <a class="nav-link{% if not tab %} active{% endif %}" href="{{ object.get_absolute_url }}">{{ object|meta:"verbose_name"|bettertitle }}</a>
   85 :     </li>
   86 : 
   87 :     {# Include any extra tabs passed by the view #}
   88 :     {% block extra_tabs %}{% endblock %}
   89 : 
   90 :     {# Include tabs for registered model views #}
   91 :      {% model_view_tabs object %} 
   92 :   </ul>
   93 : {% endblock tabs %}
   94 : 
   95 : {% block content-wrapper %}
   96 :   <div class="tab-content">
   97 :     {% block content %}{% endblock %}
   98 :   </div>
   99 : {% endblock content-wrapper %}
   100 : 
   101 : {% block modals %}


Traceback (most recent call last):
  File "/opt/netbox/lib/python3.8/site-packages/django/urls/base.py", line 78, in reverse
    extra, resolver = resolver.namespace_dict[ns]

During handling of the above exception ('netbox_dns'), another exception occurred:
  File "/opt/netbox/lib/python3.8/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/opt/netbox/lib/python3.8/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/opt/netbox/lib/python3.8/site-packages/django/views/generic/base.py", line 103, in view
    return self.dispatch(request, *args, **kwargs)
  File "/opt/netbox/netbox/netbox/views/generic/base.py", line 13, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "/opt/netbox/netbox/utilities/views.py", line 99, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "/opt/netbox/lib/python3.8/site-packages/django/views/generic/base.py", line 142, in dispatch
    return handler(request, *args, **kwargs)
  File "/opt/netbox/netbox/netbox/views/generic/object_views.py", line 70, in get
    return render(request, self.get_template_name(), {
  File "/opt/netbox/lib/python3.8/site-packages/django/shortcuts.py", line 24, in render
    content = loader.render_to_string(template_name, context, request, using=using)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/loader.py", line 62, in render_to_string
    return template.render(context, request)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/backends/django.py", line 62, in render
    return self.template.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 175, in render
    return self._render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/test/utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/loader_tags.py", line 157, in render
    return compiled_parent._render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/test/utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/loader_tags.py", line 157, in render
    return compiled_parent._render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/test/utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/loader_tags.py", line 157, in render
    return compiled_parent._render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/test/utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/loader_tags.py", line 157, in render
    return compiled_parent._render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/test/utils.py", line 111, in instrumented_test_render
    return self.nodelist.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/loader_tags.py", line 63, in render
    result = block.nodelist.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/loader_tags.py", line 63, in render
    result = block.nodelist.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "/opt/netbox/lib/python3.8/site-packages/django/template/library.py", line 258, in render
    _dict = self.func(*resolved_args, **resolved_kwargs)
  File "/opt/netbox/netbox/utilities/templatetags/tabs.py", line 53, in model_view_tabs
    'url': reverse(viewname, args=[instance.pk]),
  File "/opt/netbox/lib/python3.8/site-packages/django/urls/base.py", line 89, in reverse
    raise NoReverseMatch("%s is not a registered namespace" % key)

Exception Type: NoReverseMatch at /plugins/netbox-dns/zones/1/
Exception Value: 'netbox_dns' is not a registered namespace

The problem is caused by the following code in netbox/utilities/templatetags/tabs.py :

            if attrs := tab.render(instance):
                viewname = f"{app_label}:{model_name}_{config['name']}"

When assembled this way, viewname does not include the plugin prefix. As a result, NetBox tries to resolve the view names netbox_dns:record_journal and netbox_dns:record_changelog, which do not exist (the correct names would be plugin:netbox_dns:record_journal and plugin:netbox_dns:record_changelog).

This can be worked around by removing the tabs from the object view, but that's generally not an optimal solution as it inhibits the use of the change log and journal functionality.

Originally created by @peteeckel on GitHub (Nov 18, 2022). Originally assigned to: @jeremystretch on GitHub. ### NetBox version v3.4-beta1 ### Python version 3.8 ### Steps to Reproduce 1. Install a plugin, e.g. `netbox-dns` 2. Open an object view inheriting from `generic/object.html`, e.g. for a record ### Expected Behavior The object view opens in the GUI, displaying object details ### Observed Behavior A "NoReverseMatch" is generated for the view. ``` Environment: Request Method: GET Request URL: http://192.168.106.105/plugins/netbox-dns/zones/1/ Django Version: 4.1.2 Python Version: 3.8.11 Installed Applications: ['django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.humanize', 'corsheaders', 'debug_toolbar', 'graphiql_debug_toolbar', 'django_filters', 'django_tables2', 'django_prometheus', 'graphene_django', 'mptt', 'rest_framework', 'social_django', 'taggit', 'timezone_field', 'circuits', 'dcim', 'ipam', 'extras', 'tenancy', 'users', 'utilities', 'virtualization', 'wireless', 'django_rq', 'drf_yasg', 'netbox_dns.DNSConfig'] Installed Middleware: ['graphiql_debug_toolbar.middleware.DebugToolbarMiddleware', 'django_prometheus.middleware.PrometheusBeforeMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', 'netbox.middleware.ExceptionHandlingMiddleware', 'netbox.middleware.RemoteUserMiddleware', 'netbox.middleware.LoginRequiredMiddleware', 'netbox.middleware.DynamicConfigMiddleware', 'netbox.middleware.APIVersionMiddleware', 'netbox.middleware.ObjectChangeMiddleware', 'django_prometheus.middleware.PrometheusAfterMiddleware'] Template error: In template /opt/netbox/netbox/templates/generic/object.html, error at line 91 'netbox_dns' is not a registered namespace 81 : <ul class="nav nav-tabs px-3"> 82 : {# Primary tab #} 83 : <li class="nav-item" role="presentation"> 84 : <a class="nav-link{% if not tab %} active{% endif %}" href="{{ object.get_absolute_url }}">{{ object|meta:"verbose_name"|bettertitle }}</a> 85 : </li> 86 : 87 : {# Include any extra tabs passed by the view #} 88 : {% block extra_tabs %}{% endblock %} 89 : 90 : {# Include tabs for registered model views #} 91 : {% model_view_tabs object %} 92 : </ul> 93 : {% endblock tabs %} 94 : 95 : {% block content-wrapper %} 96 : <div class="tab-content"> 97 : {% block content %}{% endblock %} 98 : </div> 99 : {% endblock content-wrapper %} 100 : 101 : {% block modals %} Traceback (most recent call last): File "/opt/netbox/lib/python3.8/site-packages/django/urls/base.py", line 78, in reverse extra, resolver = resolver.namespace_dict[ns] During handling of the above exception ('netbox_dns'), another exception occurred: File "/opt/netbox/lib/python3.8/site-packages/django/core/handlers/exception.py", line 55, in inner response = get_response(request) File "/opt/netbox/lib/python3.8/site-packages/django/core/handlers/base.py", line 197, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/opt/netbox/lib/python3.8/site-packages/django/views/generic/base.py", line 103, in view return self.dispatch(request, *args, **kwargs) File "/opt/netbox/netbox/netbox/views/generic/base.py", line 13, in dispatch return super().dispatch(request, *args, **kwargs) File "/opt/netbox/netbox/utilities/views.py", line 99, in dispatch return super().dispatch(request, *args, **kwargs) File "/opt/netbox/lib/python3.8/site-packages/django/views/generic/base.py", line 142, in dispatch return handler(request, *args, **kwargs) File "/opt/netbox/netbox/netbox/views/generic/object_views.py", line 70, in get return render(request, self.get_template_name(), { File "/opt/netbox/lib/python3.8/site-packages/django/shortcuts.py", line 24, in render content = loader.render_to_string(template_name, context, request, using=using) File "/opt/netbox/lib/python3.8/site-packages/django/template/loader.py", line 62, in render_to_string return template.render(context, request) File "/opt/netbox/lib/python3.8/site-packages/django/template/backends/django.py", line 62, in render return self.template.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 175, in render return self._render(context) File "/opt/netbox/lib/python3.8/site-packages/django/test/utils.py", line 111, in instrumented_test_render return self.nodelist.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in render return SafeString("".join([node.render_annotated(context) for node in self])) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp> return SafeString("".join([node.render_annotated(context) for node in self])) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated return self.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/loader_tags.py", line 157, in render return compiled_parent._render(context) File "/opt/netbox/lib/python3.8/site-packages/django/test/utils.py", line 111, in instrumented_test_render return self.nodelist.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in render return SafeString("".join([node.render_annotated(context) for node in self])) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp> return SafeString("".join([node.render_annotated(context) for node in self])) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated return self.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/loader_tags.py", line 157, in render return compiled_parent._render(context) File "/opt/netbox/lib/python3.8/site-packages/django/test/utils.py", line 111, in instrumented_test_render return self.nodelist.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in render return SafeString("".join([node.render_annotated(context) for node in self])) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp> return SafeString("".join([node.render_annotated(context) for node in self])) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated return self.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/loader_tags.py", line 157, in render return compiled_parent._render(context) File "/opt/netbox/lib/python3.8/site-packages/django/test/utils.py", line 111, in instrumented_test_render return self.nodelist.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in render return SafeString("".join([node.render_annotated(context) for node in self])) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp> return SafeString("".join([node.render_annotated(context) for node in self])) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated return self.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/loader_tags.py", line 157, in render return compiled_parent._render(context) File "/opt/netbox/lib/python3.8/site-packages/django/test/utils.py", line 111, in instrumented_test_render return self.nodelist.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in render return SafeString("".join([node.render_annotated(context) for node in self])) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp> return SafeString("".join([node.render_annotated(context) for node in self])) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated return self.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/loader_tags.py", line 63, in render result = block.nodelist.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in render return SafeString("".join([node.render_annotated(context) for node in self])) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp> return SafeString("".join([node.render_annotated(context) for node in self])) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated return self.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/loader_tags.py", line 63, in render result = block.nodelist.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in render return SafeString("".join([node.render_annotated(context) for node in self])) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp> return SafeString("".join([node.render_annotated(context) for node in self])) File "/opt/netbox/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated return self.render(context) File "/opt/netbox/lib/python3.8/site-packages/django/template/library.py", line 258, in render _dict = self.func(*resolved_args, **resolved_kwargs) File "/opt/netbox/netbox/utilities/templatetags/tabs.py", line 53, in model_view_tabs 'url': reverse(viewname, args=[instance.pk]), File "/opt/netbox/lib/python3.8/site-packages/django/urls/base.py", line 89, in reverse raise NoReverseMatch("%s is not a registered namespace" % key) Exception Type: NoReverseMatch at /plugins/netbox-dns/zones/1/ Exception Value: 'netbox_dns' is not a registered namespace ``` The problem is caused by the following code in `netbox/utilities/templatetags/tabs.py `: ``` if attrs := tab.render(instance): viewname = f"{app_label}:{model_name}_{config['name']}" ``` When assembled this way, `viewname` does not include the `plugin` prefix. As a result, NetBox tries to resolve the view names `netbox_dns:record_journal` and `netbox_dns:record_changelog`, which do not exist (the correct names would be `plugin:netbox_dns:record_journal` and `plugin:netbox_dns:record_changelog`). This can be worked around by removing the tabs from the object view, but that's generally not an optimal solution as it inhibits the use of the change log and journal functionality.
adam added the type: bugstatus: acceptedbeta labels 2025-12-29 20:20:56 +01:00
adam closed this issue 2025-12-29 20:20:56 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#7256