Przeglądaj źródła

Fixes #21064: Ensures that extra choices preserve nested colons

Jason Novinger 1 miesiąc temu
rodzic
commit
5a64cb712d

+ 7 - 7
netbox/extras/forms/model_forms.py

@@ -189,22 +189,22 @@ class CustomFieldChoiceSetForm(ChangelogMessageMixin, forms.ModelForm):
         # if standardize these, we can simplify this code
 
         # Convert extra_choices Array Field from model to CharField for form
-        if 'extra_choices' in self.initial and self.initial['extra_choices']:
-            extra_choices = self.initial['extra_choices']
+        if extra_choices := self.initial.get('extra_choices', None):
             if isinstance(extra_choices, str):
                 extra_choices = [extra_choices]
-            choices = ""
+            choices = []
             for choice in extra_choices:
                 # Setup choices in Add Another use case
                 if isinstance(choice, str):
                     choice_str = ":".join(choice.replace("'", "").replace(" ", "")[1:-1].split(","))
-                    choices += choice_str + "\n"
+                    choices.append(choice_str)
                 # Setup choices in Edit use case
                 elif isinstance(choice, list):
-                    choice_str = ":".join(choice)
-                    choices += choice_str + "\n"
+                    value = choice[0].replace(':', '\\:')
+                    label = choice[1].replace(':', '\\:')
+                    choices.append(f'{value}:{label}')
 
-            self.initial['extra_choices'] = choices
+            self.initial['extra_choices'] = '\n'.join(choices)
 
     def clean_extra_choices(self):
         data = []

+ 26 - 0
netbox/extras/tests/test_forms.py

@@ -5,6 +5,7 @@ from dcim.forms import SiteForm
 from dcim.models import Site
 from extras.choices import CustomFieldTypeChoices
 from extras.forms import SavedFilterForm
+from extras.forms.model_forms import CustomFieldChoiceSetForm
 from extras.models import CustomField, CustomFieldChoiceSet
 
 
@@ -90,6 +91,31 @@ class CustomFieldModelFormTest(TestCase):
             self.assertIsNone(instance.custom_field_data[field_type])
 
 
+class CustomFieldChoiceSetFormTest(TestCase):
+
+    def test_escaped_colons_preserved_on_edit(self):
+        choice_set = CustomFieldChoiceSet.objects.create(
+            name='Test Choice Set',
+            extra_choices=[['foo:bar', 'label'], ['value', 'label:with:colons']]
+        )
+
+        form = CustomFieldChoiceSetForm(instance=choice_set)
+        initial_choices = form.initial['extra_choices']
+
+        # colons are re-escaped
+        self.assertEqual(initial_choices, 'foo\\:bar:label\nvalue:label\\:with\\:colons')
+
+        form = CustomFieldChoiceSetForm(
+            {'name': choice_set.name, 'extra_choices': initial_choices},
+            instance=choice_set
+        )
+        self.assertTrue(form.is_valid())
+        updated = form.save()
+
+        # cleaned extra choices are correct, which does actually mean a list of tuples
+        self.assertEqual(updated.extra_choices, [('foo:bar', 'label'), ('value', 'label:with:colons')])
+
+
 class SavedFilterFormTest(TestCase):
 
     def test_basic_submit(self):