diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index f3b8fae61..14cae8d9d 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -658,7 +658,7 @@ class TableConfig(CloningMixin, ChangeLoggedModel): table = self.table_class([]) # Validate ordering columns - for name in self.ordering: + for name in self.ordering or []: if name.startswith('-'): name = name[1:] # Strip leading hyphen if name not in table.columns: diff --git a/netbox/extras/tests/test_models.py b/netbox/extras/tests/test_models.py index 794f216fb..897ecd0c8 100644 --- a/netbox/extras/tests/test_models.py +++ b/netbox/extras/tests/test_models.py @@ -24,6 +24,7 @@ from extras.models import ( EventRule, ExportTemplate, ImageAttachment, + TableConfig, Tag, TaggedItem, ) @@ -31,6 +32,7 @@ from extras.models.mixins import RenderTemplateMixin from tenancy.models import Tenant, TenantGroup from utilities.exceptions import AbortRequest from utilities.jinja2 import render_jinja2 +from utilities.tables import get_table_for_model from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine @@ -190,6 +192,25 @@ class ImageAttachmentTestCase(TestCase): self.assertCountEqual(storage.files.keys(), {base_name, suffixed_name}) +class TableConfigTestCase(TestCase): + @classmethod + def setUpTestData(cls): + cls.site_ct = ContentType.objects.get_for_model(Site) + cls.table_name = get_table_for_model(Site).__name__ + + def test_clean_accepts_ordering_none(self): + """clean() must accept ordering=None (field is null=True).""" + tc = TableConfig( + object_type=self.site_ct, + table=self.table_name, + name='No ordering', + columns=['name'], + # ordering left unset (defaults to None) + ) + # Must not raise TypeError: 'NoneType' object is not iterable + tc.full_clean() + + class TagTestCase(TestCase): def test_default_ordering_weight_then_name_is_set(self):