mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-09 01:49:35 -06:00
Fixes #20641: Handle viewsets with queryset=None in get_view_name() (#20642)
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
CodeQL / Analyze (${{ matrix.language }}) (none, actions) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, python) (push) Waiting to run
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
CodeQL / Analyze (${{ matrix.language }}) (none, actions) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, python) (push) Waiting to run
The get_view_name() utility function crashed with AttributeError when called on viewsets that override get_queryset() without setting a class-level queryset attribute (e.g., ObjectChangeViewSet). This pattern became necessary in #20089 to force re-evaluation of valid_models() on each request, ensuring ObjectChange querysets reflect current ContentType state. Added None check to fall back to DRF's default view naming when no class-level queryset exists.
This commit is contained in:
parent
ae5d7911f9
commit
fb8d41b527
@ -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()`.
|
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.
|
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.
|
# Derive the model name from the queryset.
|
||||||
name = title(view.queryset.model._meta.verbose_name)
|
name = title(view.queryset.model._meta.verbose_name)
|
||||||
if suffix := getattr(view, 'suffix', None):
|
if suffix := getattr(view, 'suffix', None):
|
||||||
|
|||||||
@ -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 django.urls import reverse
|
||||||
from drf_spectacular.drainage import GENERATOR_STATS
|
from drf_spectacular.drainage import GENERATOR_STATS
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
@ -9,6 +9,7 @@ from extras.choices import CustomFieldTypeChoices
|
|||||||
from extras.models import CustomField
|
from extras.models import CustomField
|
||||||
from ipam.models import VLAN
|
from ipam.models import VLAN
|
||||||
from netbox.config import get_config
|
from netbox.config import get_config
|
||||||
|
from utilities.api import get_view_name
|
||||||
from utilities.testing import APITestCase, disable_warnings
|
from utilities.testing import APITestCase, disable_warnings
|
||||||
|
|
||||||
|
|
||||||
@ -267,3 +268,19 @@ class APIDocsTestCase(TestCase):
|
|||||||
with GENERATOR_STATS.silence(): # Suppress schema generator warnings
|
with GENERATOR_STATS.silence(): # Suppress schema generator warnings
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.status_code, 200)
|
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')
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user