diff --git a/netbox/utilities/api.py b/netbox/utilities/api.py index 0aec1daef94..8d9e511ebb0 100644 --- a/netbox/utilities/api.py +++ b/netbox/utilities/api.py @@ -72,7 +72,7 @@ def get_view_name(view): Derive the view name from its associated model, if it has one. Fall back to DRF's built-in `get_view_name()`. This function is provided to DRF as its VIEW_NAME_FUNCTION. """ - if hasattr(view, 'queryset'): + if hasattr(view, 'queryset') and view.queryset is not None: # Derive the model name from the queryset. name = title(view.queryset.model._meta.verbose_name) if suffix := getattr(view, 'suffix', None): diff --git a/netbox/utilities/tests/test_api.py b/netbox/utilities/tests/test_api.py index f36827d8527..885f71a7398 100644 --- a/netbox/utilities/tests/test_api.py +++ b/netbox/utilities/tests/test_api.py @@ -1,4 +1,4 @@ -from django.test import Client, TestCase, override_settings +from django.test import Client, TestCase, override_settings, tag from django.urls import reverse from drf_spectacular.drainage import GENERATOR_STATS from rest_framework import status @@ -9,6 +9,7 @@ from extras.models import CustomField from ipam.models import VLAN from netbox.config import get_config +from utilities.api import get_view_name from utilities.testing import APITestCase, disable_warnings @@ -267,3 +268,19 @@ def test_api_docs(self): with GENERATOR_STATS.silence(): # Suppress schema generator warnings response = self.client.get(url) self.assertEqual(response.status_code, 200) + + +class GetViewNameTestCase(TestCase): + + @tag('regression') + def test_get_view_name_with_none_queryset(self): + from rest_framework.viewsets import ReadOnlyModelViewSet + + class MockViewSet(ReadOnlyModelViewSet): + queryset = None + + view = MockViewSet() + view.suffix = 'List' + + name = get_view_name(view) + self.assertEqual(name, 'Mock List')