-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Description
NetBox version
v3.4-beta1
Python version
3.8
Steps to Reproduce
- Install and configure a plugin (e.g.
netbox-dns) - Open a detailed object view for an object without a defined ObjectJournalView in the GUI (after commit 84c0c45 has been applied, ref. 3.4-beta1: Plugin object detail views with tabs cause exceptions #10980)
Expected Behavior
The object detail view is displayed for the instance.
Observed Behavior
The object detail view still is not displayed, instead there is a new NoReverseMatch exception:
Environment:
Request Method: GET
Request URL: http://192.168.106.105/plugins/netbox-dns/nameservers/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
Reverse for 'nameserver_journal' not found. 'nameserver_journal' is not a valid view function or pattern name.
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/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 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 40, in model_view_tabs
'url': reverse(viewname, args=[instance.pk]),
File "/opt/netbox/lib/python3.8/site-packages/django/urls/base.py", line 95, in reverse
return resolver._reverse_with_prefix(view, prefix, *args, **kwargs)
File "/opt/netbox/lib/python3.8/site-packages/django/urls/resolvers.py", line 828, in _reverse_with_prefix
raise NoReverseMatch(msg)
Exception Type: NoReverseMatch at /plugins/netbox-dns/nameservers/1/
Exception Value: Reverse for 'nameserver_journal' not found. 'nameserver_journal' is not a valid view function or pattern name.
This is caused by the missing of a nameserver_journal view for the NameServer model. The current implementation of model_view_tags seems to assume that there is at least an ObjectChangeLogView and an ObjectJournalView defined for each object model and tries to render both, and throws an exception if one of them is missing.
It can be worked around by always defining both for all models that have a detail view template inheriting from generic/object.html or by disabling tabs in the inheriting template code.
Either NetBox should detect the presence of an ObjectChangeLogView and ObjectJournalView for a model and handle either one's absence gracefully (i.e. by ignoring it), or there should be an option to disable those views and a mention of the change in behaviour as a breaking change. The former is probably the better option.