From b23ac303cd2b47622dff0f906b48177365d1a41e Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 20 Sep 2022 08:50:20 -0700 Subject: [PATCH] 10348 add decimal custom field --- netbox/extras/models/customfields.py | 4 +-- netbox/extras/tests/test_customfields.py | 42 ++++++++++++++++++++++++ netbox/extras/tests/test_forms.py | 3 ++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index 0cbfcc909..170509ec4 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -219,11 +219,11 @@ class CustomField(CloningMixin, ExportTemplatesMixin, WebhooksMixin, ChangeLogge }) # Minimum/maximum values can be set only for numeric fields - if self.validation_minimum is not None and self.type != CustomFieldTypeChoices.TYPE_INTEGER: + if self.validation_minimum is not None and (self.type != CustomFieldTypeChoices.TYPE_INTEGER and self.type != CustomFieldTypeChoices.TYPE_DECIMAL): raise ValidationError({ 'validation_minimum': "A minimum value may be set only for numeric fields" }) - if self.validation_maximum is not None and self.type != CustomFieldTypeChoices.TYPE_INTEGER: + if self.validation_maximum is not None and (self.type != CustomFieldTypeChoices.TYPE_INTEGER and self.type != CustomFieldTypeChoices.TYPE_DECIMAL): raise ValidationError({ 'validation_maximum': "A maximum value may be set only for numeric fields" }) diff --git a/netbox/extras/tests/test_customfields.py b/netbox/extras/tests/test_customfields.py index 946999bc2..4aa63defc 100644 --- a/netbox/extras/tests/test_customfields.py +++ b/netbox/extras/tests/test_customfields.py @@ -102,6 +102,32 @@ class CustomFieldTest(TestCase): instance.refresh_from_db() self.assertIsNone(instance.custom_field_data.get(cf.name)) + def test_decimal_field(self): + + # Create a custom field & check that initial value is null + cf = CustomField.objects.create( + name='decimal_field', + type=CustomFieldTypeChoices.TYPE_DECIMAL, + required=False + ) + cf.content_types.set([self.object_type]) + instance = Site.objects.first() + self.assertIsNone(instance.custom_field_data[cf.name]) + + for value in (123456.54, 0, -123456.78): + + # Assign a value and check that it is saved + instance.custom_field_data[cf.name] = value + instance.save() + instance.refresh_from_db() + self.assertEqual(instance.custom_field_data[cf.name], value) + + # Delete the stored value and check that it is now null + instance.custom_field_data.pop(cf.name) + instance.save() + instance.refresh_from_db() + self.assertIsNone(instance.custom_field_data.get(cf.name)) + def test_boolean_field(self): # Create a custom field & check that initial value is null @@ -1096,6 +1122,11 @@ class CustomFieldModelFilterTest(TestCase): cf.save() cf.content_types.set([obj_type]) + # Decimal filtering + cf = CustomField(name='cf12', type=CustomFieldTypeChoices.TYPE_DECIMAL) + cf.save() + cf.content_types.set([obj_type]) + Site.objects.bulk_create([ Site(name='Site 1', slug='site-1', custom_field_data={ 'cf1': 100, @@ -1109,6 +1140,7 @@ class CustomFieldModelFilterTest(TestCase): 'cf9': ['A', 'X'], 'cf10': manufacturers[0].pk, 'cf11': [manufacturers[0].pk, manufacturers[3].pk], + 'cf12': 100.25, }), Site(name='Site 2', slug='site-2', custom_field_data={ 'cf1': 200, @@ -1122,6 +1154,7 @@ class CustomFieldModelFilterTest(TestCase): 'cf9': ['B', 'X'], 'cf10': manufacturers[1].pk, 'cf11': [manufacturers[1].pk, manufacturers[3].pk], + 'cf12': 200.25, }), Site(name='Site 3', slug='site-3', custom_field_data={ 'cf1': 300, @@ -1135,6 +1168,7 @@ class CustomFieldModelFilterTest(TestCase): 'cf9': ['C', 'X'], 'cf10': manufacturers[2].pk, 'cf11': [manufacturers[2].pk, manufacturers[3].pk], + 'cf12': 300.25, }), ]) @@ -1146,6 +1180,14 @@ class CustomFieldModelFilterTest(TestCase): self.assertEqual(self.filterset({'cf_cf1__lt': [200]}, self.queryset).qs.count(), 1) self.assertEqual(self.filterset({'cf_cf1__lte': [200]}, self.queryset).qs.count(), 2) + def test_filter_decimal(self): + self.assertEqual(self.filterset({'cf_cf12': [100.25, 200.25]}, self.queryset).qs.count(), 2) + self.assertEqual(self.filterset({'cf_cf12__n': [200.25]}, self.queryset).qs.count(), 2) + self.assertEqual(self.filterset({'cf_cf12__gt': [200.25]}, self.queryset).qs.count(), 1) + self.assertEqual(self.filterset({'cf_cf12__gte': [200.25]}, self.queryset).qs.count(), 2) + self.assertEqual(self.filterset({'cf_cf12__lt': [200.25]}, self.queryset).qs.count(), 1) + self.assertEqual(self.filterset({'cf_cf12__lte': [200.25]}, self.queryset).qs.count(), 2) + def test_filter_boolean(self): self.assertEqual(self.filterset({'cf_cf2': True}, self.queryset).qs.count(), 2) self.assertEqual(self.filterset({'cf_cf2': False}, self.queryset).qs.count(), 1) diff --git a/netbox/extras/tests/test_forms.py b/netbox/extras/tests/test_forms.py index 1ec50b7dd..35402bda3 100644 --- a/netbox/extras/tests/test_forms.py +++ b/netbox/extras/tests/test_forms.py @@ -23,6 +23,9 @@ class CustomFieldModelFormTest(TestCase): cf_integer = CustomField.objects.create(name='integer', type=CustomFieldTypeChoices.TYPE_INTEGER) cf_integer.content_types.set([obj_type]) + cf_integer = CustomField.objects.create(name='decimal', type=CustomFieldTypeChoices.TYPE_DECIMAL) + cf_integer.content_types.set([obj_type]) + cf_boolean = CustomField.objects.create(name='boolean', type=CustomFieldTypeChoices.TYPE_BOOLEAN) cf_boolean.content_types.set([obj_type])