ソースを参照

Closes #19945: Create DecimalVar class for custom script input (#19963)

Kyer 6 ヶ月 前
コミット
89a94486e1

+ 9 - 0
docs/customization/custom-scripts.md

@@ -275,6 +275,15 @@ Stores a numeric integer. Options include:
 * `min_value` - Minimum value
 * `max_value` - Maximum value
 
+### DecimalVar
+
+Stores a numeric decimal. Options include:
+
+* `min_value` - Minimum value
+* `max_value` - Maximum value
+* `max_digits` - Maximum number of digits, including decimal places
+* `decimal_places` - Number of decimal places
+
 ### BooleanVar
 
 A true/false flag. This field has no options beyond the defaults listed above.

+ 21 - 0
netbox/extras/scripts.py

@@ -31,6 +31,7 @@ __all__ = (
     'DateTimeVar',
     'FileVar',
     'IntegerVar',
+    'DecimalVar',
     'IPAddressVar',
     'IPAddressWithMaskVar',
     'IPNetworkVar',
@@ -135,6 +136,26 @@ class IntegerVar(ScriptVariable):
             self.field_attrs['max_value'] = max_value
 
 
+class DecimalVar(ScriptVariable):
+    """
+    Decimal representation. Can enforce minimum/maximum values, maximum digits and decimal places.
+    """
+    form_field = forms.DecimalField
+
+    def __init__(self, min_value=None, max_value=None, max_digits=None, decimal_places=None, *args, **kwargs,):
+        super().__init__(*args, **kwargs)
+
+        # Optional constraints
+        if min_value:
+            self.field_attrs["min_value"] = min_value
+        if max_value:
+            self.field_attrs["max_value"] = max_value
+        if max_digits:
+            self.field_attrs["max_digits"] = max_digits
+        if decimal_places:
+            self.field_attrs["decimal_places"] = decimal_places
+
+
 class BooleanVar(ScriptVariable):
     """
     Boolean representation (true/false). Renders as a checkbox.

+ 49 - 0
netbox/extras/tests/test_scripts.py

@@ -1,6 +1,7 @@
 import logging
 import tempfile
 from datetime import date, datetime, timezone
+from decimal import Decimal
 
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.test import TestCase
@@ -138,6 +139,54 @@ class ScriptVariablesTest(TestCase):
         self.assertTrue(form.is_valid())
         self.assertEqual(form.cleaned_data['var1'], data['var1'])
 
+    def test_decimalvar(self):
+
+        class TestScript(Script):
+
+            var1 = DecimalVar(
+                min_value=-100.500,
+                max_value=100.500,
+                max_digits=6,
+                decimal_places=3,
+                required=False
+            )
+
+            var2 = DecimalVar(
+                max_digits=3,
+                decimal_places=1,
+                required=False
+            )
+
+        # Validate min_value enforcement
+        data = {'var1': -100.501}
+        form = TestScript().as_form(data, None)
+        self.assertFalse(form.is_valid())
+        self.assertIn('var1', form.errors)
+
+        # Validate max_value enforcement
+        data = {'var1': 100.501}
+        form = TestScript().as_form(data, None)
+        self.assertFalse(form.is_valid())
+        self.assertIn('var1', form.errors)
+
+        # Validate max_digits enforcement
+        data = {'var2': 123.4}
+        form = TestScript().as_form(data, None)
+        self.assertFalse(form.is_valid())
+        self.assertIn('var2', form.errors)
+
+        # Validate decimal_places
+        data = {'var2': 1.23}
+        form = TestScript().as_form(data, None)
+        self.assertFalse(form.is_valid())
+        self.assertIn('var2', form.errors)
+
+        # Validate valid data
+        data = {'var1': '50.123'}
+        form = TestScript().as_form(data, None)
+        self.assertTrue(form.is_valid())
+        self.assertEqual(form.cleaned_data['var1'], Decimal(data['var1']))
+
     def test_booleanvar(self):
 
         class TestScript(Script):