Fixes #19986: Fix plugin list view button URLs (#20019)
Some checks are pending
CI / build (20.x, 3.10) (push) Waiting to run
CI / build (20.x, 3.11) (push) Waiting to run
CI / build (20.x, 3.12) (push) Waiting to run

* Fixes #19986: Fix plugin list view button URLs

Plugin list view action buttons (Add, Import, Export) were generating 404
errors because ObjectAction.get_url() was manually constructing viewnames
without the required "plugins:" namespace prefix for plugin models.

Replace manual viewname construction with NetBox's get_viewname() utility
function, which properly handles plugin detection and namespace prefixing
for both core and plugin models.

* Ensure expected URL patterns are registered, ensures tests pass
This commit is contained in:
Jason Novinger 2025-08-05 07:26:43 -05:00 committed by GitHub
parent 2b7600e659
commit 0c70e9e140
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 37 additions and 1 deletions

View File

@ -6,6 +6,7 @@ from django.utils.translation import gettext as _
from core.models import ObjectType from core.models import ObjectType
from extras.models import ExportTemplate from extras.models import ExportTemplate
from utilities.querydict import prepare_cloned_fields from utilities.querydict import prepare_cloned_fields
from utilities.views import get_viewname
__all__ = ( __all__ = (
'AddObject', 'AddObject',
@ -42,7 +43,7 @@ class ObjectAction:
@classmethod @classmethod
def get_url(cls, obj): def get_url(cls, obj):
viewname = f'{obj._meta.app_label}:{obj._meta.model_name}_{cls.name}' viewname = get_viewname(obj, action=cls.name)
kwargs = { kwargs = {
kwarg: getattr(obj, kwarg) for kwarg in cls.url_kwargs kwarg: getattr(obj, kwarg) for kwarg in cls.url_kwargs
} }

View File

@ -7,5 +7,8 @@ urlpatterns = (
path('models/', views.DummyModelsView.as_view(), name='dummy_model_list'), path('models/', views.DummyModelsView.as_view(), name='dummy_model_list'),
path('models/add/', views.DummyModelAddView.as_view(), name='dummy_model_add'), path('models/add/', views.DummyModelAddView.as_view(), name='dummy_model_add'),
path('netboxmodel/', views.DummyNetBoxModelView.as_view(), name='dummynetboxmodel_list'),
path('netboxmodel/add/', views.DummyNetBoxModelView.as_view(), name='dummynetboxmodel_add'),
path('netboxmodel/import/', views.DummyNetBoxModelView.as_view(), name='dummynetboxmodel_bulk_import'),
path('netboxmodel/<int:pk>/', views.DummyNetBoxModelView.as_view(), name='dummynetboxmodel'), path('netboxmodel/<int:pk>/', views.DummyNetBoxModelView.as_view(), name='dummynetboxmodel'),
) )

View File

@ -0,0 +1,32 @@
from unittest import skipIf
from django.conf import settings
from django.test import TestCase
from dcim.models import Device
from netbox.object_actions import AddObject, BulkImport
from netbox.tests.dummy_plugin.models import DummyNetBoxModel
class ObjectActionTest(TestCase):
def test_get_url_core_model(self):
"""Test URL generation for core NetBox models"""
obj = Device()
url = AddObject.get_url(obj)
self.assertEqual(url, '/dcim/devices/add/')
url = BulkImport.get_url(obj)
self.assertEqual(url, '/dcim/devices/import/')
@skipIf('netbox.tests.dummy_plugin' not in settings.PLUGINS, "dummy_plugin not in settings.PLUGINS")
def test_get_url_plugin_model(self):
"""Test URL generation for plugin models includes plugins: namespace"""
obj = DummyNetBoxModel()
url = AddObject.get_url(obj)
self.assertEqual(url, '/plugins/dummy-plugin/netboxmodel/add/')
url = BulkImport.get_url(obj)
self.assertEqual(url, '/plugins/dummy-plugin/netboxmodel/import/')