Can't use default ObjectListView template from plugin #4169

Closed
opened 2025-12-29 18:33:36 +01:00 by adam · 1 comment
Owner

Originally created by @mngan on GitHub (Oct 7, 2020).

Environment

  • Python version: 3.8.2
  • NetBox version: 2.9.4

I am trying to define a view in a plugin derived from ObjectListView, and I was intending on just using the default template (obj_list.html), but in the obj_list.html it uses a filter validated_viewname which returns a viewname in the form app:model. However, plugin views need to have the plugins: prefix. So the view name generated will not be able to resolve to a URL via reverse(), and returns None. This is then passed to the add_button filter which throws an exception if passed None.

Steps to Reproduce

  1. Set up a basic list view in a plugin
    class SupportContractListView(ObjectListView):
       queryset = SupportContract.objects.order_by(*SupportContract._meta.ordering)
       filterset = SupportContractFilterSet
       filter_form = SupportContractFilterForm
       table = SupportContractTable
  1. Setup all the necessary url paths
urlpatterns = [
    path('contracts/', views.SupportContractListView.as_view(), name='supportcontract_list'),
    path('contracts/add', views.SupportContractListView.as_view(), name='supportcontract_add'),
    path('contracts/import', views.SupportContractListView.as_view(), name='supportcontract_import'),
    path('contracts/bulk/edit', views.SupportContractListView.as_view(), name='supportcontract_bulk_edit'),
    path('contracts/bulk/delete', views.SupportContractListView.as_view(), name='supportcontract_bulk_delete'),
    path('contracts/<int:pk>', views.SupportContractView.as_view(), name='supportcontract'),
]
  1. Attempt to view the contracts list view http:///plugins/support-contracts/contracts

Expected Behavior

One would expect the page to render.

Observed Behavior

Instead, you get a server error thrown by utilities.templatetags.buttons.add_button() because url is None. Assuming this is something desirable to support, it would seem validated_viewname() should test the viewname with the plugins: prefix as well. If you do this then the page will render as expected. There are possibly other related functions that would need the treatment, but I haven't gotten that far in my investigation. I did notice that all the plugins on github get around this by using their own templates for the ObjectListView.

Internal Server Error: /plugins/support-contracts/contracts/
Traceback (most recent call last):
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 179, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/views/generic/base.py", line 73, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/milton/netbox/netbox/utilities/views.py", line 123, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/views/generic/base.py", line 101, in dispatch
    return handler(request, *args, **kwargs)
  File "/home/milton/netbox/netbox/utilities/views.py", line 330, in get
    return render(request, self.template_name, context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/shortcuts.py", line 19, in render
    content = loader.render_to_string(template_name, context, request, using=using)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/loader.py", line 62, in render_to_string
    return template.render(context, request)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/backends/django.py", line 61, in render
    return self.template.render(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 170, in render
    return self._render(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/test/utils.py", line 96, in instrumented_test_render
    return self.nodelist.render(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 938, in render
    bit = node.render_annotated(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 905, in render_annotated
    return self.render(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/loader_tags.py", line 150, in render
    return compiled_parent._render(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/test/utils.py", line 96, in instrumented_test_render
    return self.nodelist.render(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 938, in render
    bit = node.render_annotated(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 905, in render_annotated
    return self.render(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/loader_tags.py", line 62, in render
    result = block.nodelist.render(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 938, in render
    bit = node.render_annotated(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 905, in render_annotated
    return self.render(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/defaulttags.py", line 312, in render
    return nodelist.render(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 938, in render
    bit = node.render_annotated(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 905, in render_annotated
    return self.render(context)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/library.py", line 214, in render
    _dict = self.func(*resolved_args, **resolved_kwargs)
  File "/home/milton/netbox/netbox/utilities/templatetags/buttons.py", line 83, in add_button
    url = reverse(url)
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/urls/base.py", line 87, in reverse
    return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs))
  File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/urls/resolvers.py", line 685, in _reverse_with_prefix
    raise NoReverseMatch(msg)
django.urls.exceptions.NoReverseMatch: Reverse for 'None' not found. 'None' is not a valid view function or pattern name.
Originally created by @mngan on GitHub (Oct 7, 2020). <!-- NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED. This form is only for reproducible bugs. If you need assistance with NetBox installation, or if you have a general question, DO NOT open an issue. Instead, post to our mailing list: https://groups.google.com/forum/#!forum/netbox-discuss Please describe the environment in which you are running NetBox. Be sure that you are running an unmodified instance of the latest stable release before submitting a bug report, and that any plugins have been disabled. --> ### Environment * Python version: 3.8.2 * NetBox version: 2.9.4 I am trying to define a view in a plugin derived from ObjectListView, and I was intending on just using the default template (obj_list.html), but in the obj_list.html it uses a filter validated_viewname which returns a viewname in the form `app:model`. However, plugin views need to have the `plugins:` prefix. So the view name generated will not be able to resolve to a URL via `reverse()`, and returns `None`. This is then passed to the `add_button` filter which throws an exception if passed `None`. ### Steps to Reproduce 1. Set up a basic list view in a plugin ``` class SupportContractListView(ObjectListView): queryset = SupportContract.objects.order_by(*SupportContract._meta.ordering) filterset = SupportContractFilterSet filter_form = SupportContractFilterForm table = SupportContractTable ``` 2. Setup all the necessary url paths ``` urlpatterns = [ path('contracts/', views.SupportContractListView.as_view(), name='supportcontract_list'), path('contracts/add', views.SupportContractListView.as_view(), name='supportcontract_add'), path('contracts/import', views.SupportContractListView.as_view(), name='supportcontract_import'), path('contracts/bulk/edit', views.SupportContractListView.as_view(), name='supportcontract_bulk_edit'), path('contracts/bulk/delete', views.SupportContractListView.as_view(), name='supportcontract_bulk_delete'), path('contracts/<int:pk>', views.SupportContractView.as_view(), name='supportcontract'), ] ``` 3. Attempt to view the contracts list view http://<server>/plugins/support-contracts/contracts <!-- What did you expect to happen? --> ### Expected Behavior One would expect the page to render. <!-- What happened instead? --> ### Observed Behavior Instead, you get a server error thrown by utilities.templatetags.buttons.add_button() because url is `None`. Assuming this is something desirable to support, it would seem validated_viewname() should test the viewname with the `plugins:` prefix as well. If you do this then the page will render as expected. There are possibly other related functions that would need the treatment, but I haven't gotten that far in my investigation. I did notice that all the plugins on github get around this by using their own templates for the ObjectListView. ``` Internal Server Error: /plugins/support-contracts/contracts/ Traceback (most recent call last): File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner response = get_response(request) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 179, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/views/generic/base.py", line 73, in view return self.dispatch(request, *args, **kwargs) File "/home/milton/netbox/netbox/utilities/views.py", line 123, in dispatch return super().dispatch(request, *args, **kwargs) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/views/generic/base.py", line 101, in dispatch return handler(request, *args, **kwargs) File "/home/milton/netbox/netbox/utilities/views.py", line 330, in get return render(request, self.template_name, context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/shortcuts.py", line 19, in render content = loader.render_to_string(template_name, context, request, using=using) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/loader.py", line 62, in render_to_string return template.render(context, request) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/backends/django.py", line 61, in render return self.template.render(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 170, in render return self._render(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/test/utils.py", line 96, in instrumented_test_render return self.nodelist.render(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 938, in render bit = node.render_annotated(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 905, in render_annotated return self.render(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/loader_tags.py", line 150, in render return compiled_parent._render(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/test/utils.py", line 96, in instrumented_test_render return self.nodelist.render(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 938, in render bit = node.render_annotated(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 905, in render_annotated return self.render(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/loader_tags.py", line 62, in render result = block.nodelist.render(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 938, in render bit = node.render_annotated(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 905, in render_annotated return self.render(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/defaulttags.py", line 312, in render return nodelist.render(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 938, in render bit = node.render_annotated(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/base.py", line 905, in render_annotated return self.render(context) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/template/library.py", line 214, in render _dict = self.func(*resolved_args, **resolved_kwargs) File "/home/milton/netbox/netbox/utilities/templatetags/buttons.py", line 83, in add_button url = reverse(url) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/urls/base.py", line 87, in reverse return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)) File "/home/milton/netbox/venv/lib/python3.8/site-packages/django/urls/resolvers.py", line 685, in _reverse_with_prefix raise NoReverseMatch(msg) django.urls.exceptions.NoReverseMatch: Reverse for 'None' not found. 'None' is not a valid view function or pattern name. ```
adam closed this issue 2025-12-29 18:33:36 +01:00
Author
Owner

@jeremystretch commented on GitHub (Oct 7, 2020):

The ObjectListView class is not part of the plugins API and not supported for use outside of NetBox's core code base. Please see the plugins development documentation for a complete list of resources available to plugins developers. For help writing custom views, please consult the Django documentation.

@jeremystretch commented on GitHub (Oct 7, 2020): The ObjectListView class is not part of the plugins API and not supported for use outside of NetBox's core code base. Please see [the plugins development documentation](https://netbox.readthedocs.io/en/stable/plugins/development/) for a complete list of resources available to plugins developers. For help writing custom views, please consult the [Django documentation](https://docs.djangoproject.com/en/3.1/topics/class-based-views/).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#4169