3.4-beta1: Plugin objects without ObjectJournalView cause exceptions #7260

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

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

Originally assigned to: @jeremystretch on GitHub.

NetBox version

v3.4-beta1

Python version

3.8

Steps to Reproduce

  1. Install and configure a plugin (e.g. netbox-dns)
  2. Open a detailed object view for an object without a defined ObjectJournalView in the GUI (after commit 84c0c45 has been applied, ref. #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.

Originally created by @peteeckel on GitHub (Nov 19, 2022). Originally assigned to: @jeremystretch on GitHub. ### NetBox version v3.4-beta1 ### Python version 3.8 ### Steps to Reproduce 1. Install and configure a plugin (e.g. `netbox-dns`) 2. Open a detailed object view for an object without a defined ObjectJournalView in the GUI (after commit 84c0c45 has been applied, ref. #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.
adam added the type: bugstatus: acceptedbeta labels 2025-12-29 20:20:57 +01:00
adam closed this issue 2025-12-29 20:20:57 +01:00
Author
Owner

@peteeckel commented on GitHub (Nov 20, 2022):

I suspect this is caused by #8485. My models are inheriting from NetBoxModel, and so they now get the JournalingMixin 'for free' and are registered for the journaling feature. However, without the URLs required for it they can't handle the request.

So either I'll probably add journaling (which makes sense for some of the models, but not all), or I'll assemble the model classes myself from the base model class and the mixins. Anyway, as this can break unsuspecting plugins it should be documented.

@peteeckel commented on GitHub (Nov 20, 2022): I suspect this is caused by #8485. My models are inheriting from `NetBoxModel`, and so they now get the `JournalingMixin` 'for free' and are registered for the journaling feature. However, without the URLs required for it they can't handle the request. So either I'll probably add journaling (which makes sense for some of the models, but not all), or I'll assemble the model classes myself from the base model class and the mixins. Anyway, as this can break unsuspecting plugins it should be documented.
Author
Owner

@samk-acw commented on GitHub (Nov 21, 2022):

I'm getting a very similar error with my own plugin at the same point/function, but instead I get the " is not a registered namespace" exception. Not sure where the issue is as I had both changelog and journal URLs working on 3.3

@samk-acw commented on GitHub (Nov 21, 2022): I'm getting a very similar error with my own plugin at the same point/function, but instead I get the "<plugin name> is not a registered namespace" exception. Not sure where the issue is as I had both changelog and journal URLs working on 3.3
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#7260