diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index 85d703012..6a5c682bc 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -1,5 +1,6 @@ import re from datetime import datetime, date +import decimal import django_filters from django import forms @@ -488,7 +489,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, WebhooksMixin, ChangeLogge raise ValidationError(f"Value must match regex '{self.validation_regex}'") # Validate integer - if self.type == CustomFieldTypeChoices.TYPE_INTEGER: + elif self.type == CustomFieldTypeChoices.TYPE_INTEGER: if type(value) is not int: raise ValidationError("Value must be an integer.") if self.validation_minimum is not None and value < self.validation_minimum: @@ -497,20 +498,22 @@ class CustomField(CloningMixin, ExportTemplatesMixin, WebhooksMixin, ChangeLogge raise ValidationError(f"Value must not exceed {self.validation_maximum}") # Validate decimal - if self.type == CustomFieldTypeChoices.TYPE_DECIMAL: - if type(value) is not str: + elif self.type == CustomFieldTypeChoices.TYPE_DECIMAL: + if type(value) is not decimal.Decimal: raise ValidationError("Value must be a decimal.") - if self.validation_minimum is not None and value < self.validation_minimum: + + converted = decimal.Decimal(value) + if self.validation_minimum is not None and converted < self.validation_minimum: raise ValidationError(f"Value must be at least {self.validation_minimum}") - if self.validation_maximum is not None and value > self.validation_maximum: + if self.validation_maximum is not None and converted > self.validation_maximum: raise ValidationError(f"Value must not exceed {self.validation_maximum}") # Validate boolean - if self.type == CustomFieldTypeChoices.TYPE_BOOLEAN and value not in [True, False, 1, 0]: + elif self.type == CustomFieldTypeChoices.TYPE_BOOLEAN and value not in [True, False, 1, 0]: raise ValidationError("Value must be true or false.") # Validate date - if self.type == CustomFieldTypeChoices.TYPE_DATE: + elif self.type == CustomFieldTypeChoices.TYPE_DATE: if type(value) is not date: try: datetime.strptime(value, '%Y-%m-%d') @@ -518,14 +521,14 @@ class CustomField(CloningMixin, ExportTemplatesMixin, WebhooksMixin, ChangeLogge raise ValidationError("Date values must be in the format YYYY-MM-DD.") # Validate selected choice - if self.type == CustomFieldTypeChoices.TYPE_SELECT: + elif self.type == CustomFieldTypeChoices.TYPE_SELECT: if value not in self.choices: raise ValidationError( f"Invalid choice ({value}). Available choices are: {', '.join(self.choices)}" ) # Validate all selected choices - if self.type == CustomFieldTypeChoices.TYPE_MULTISELECT: + elif self.type == CustomFieldTypeChoices.TYPE_MULTISELECT: if not set(value).issubset(self.choices): raise ValidationError( f"Invalid choice(s) ({', '.join(value)}). Available choices are: {', '.join(self.choices)}" diff --git a/netbox/extras/tests/test_customfields.py b/netbox/extras/tests/test_customfields.py index 22411d436..2efb4a873 100644 --- a/netbox/extras/tests/test_customfields.py +++ b/netbox/extras/tests/test_customfields.py @@ -476,7 +476,7 @@ class CustomFieldAPITest(APITestCase): CustomFieldTypeChoices.TYPE_MULTISELECT: 'array', CustomFieldTypeChoices.TYPE_OBJECT: 'object', CustomFieldTypeChoices.TYPE_MULTIOBJECT: 'array', - CustomFieldTypeChoices.TYPE_DECIMAL: 'decimal', + CustomFieldTypeChoices.TYPE_git: 'decimal', } self.add_permissions('extras.view_customfield') @@ -1201,8 +1201,8 @@ class CustomFieldModelFilterTest(TestCase): 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': [100.25, 200.25]}, self.queryset).qs.count(), 3) + self.assertEqual(self.filterset({'cf_cf12__n': [200.25]}, self.queryset).qs.count(), 3) 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)