Przeglądaj źródła

13983 Add nested arrays for extra_choices in CustomFieldChoiceSet (#14470)

* 13983 split array fields in CSV data for CustomFieldChoices

* 13983 fix help text

* 13983 update tests

* 13983 use re for split

* 13983 replace escaped chars

* 13983 fix escape handling

* 13983 fix escape handling

* 13983 fix escape handling
Arthur Hanson 2 lat temu
rodzic
commit
69bf1472d2

+ 19 - 1
netbox/extras/forms/bulk_import.py

@@ -1,3 +1,5 @@
+import re
+
 from django import forms
 from django import forms
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.postgres.forms import SimpleArrayField
 from django.contrib.postgres.forms import SimpleArrayField
@@ -76,7 +78,10 @@ class CustomFieldChoiceSetImportForm(CSVModelForm):
     extra_choices = SimpleArrayField(
     extra_choices = SimpleArrayField(
         base_field=forms.CharField(),
         base_field=forms.CharField(),
         required=False,
         required=False,
-        help_text=_('Comma-separated list of field choices')
+        help_text=_(
+            'Quoted string of comma-separated field choices with optional labels separated by colon: '
+            '"choice1:First Choice,choice2:Second Choice"'
+        )
     )
     )
 
 
     class Meta:
     class Meta:
@@ -85,6 +90,19 @@ class CustomFieldChoiceSetImportForm(CSVModelForm):
             'name', 'description', 'extra_choices', 'order_alphabetically',
             'name', 'description', 'extra_choices', 'order_alphabetically',
         )
         )
 
 
+    def clean_extra_choices(self):
+        if isinstance(self.cleaned_data['extra_choices'], list):
+            data = []
+            for line in self.cleaned_data['extra_choices']:
+                try:
+                    value, label = re.split(r'(?<!\\):', line, maxsplit=1)
+                    value = value.replace('\\:', ':')
+                    label = label.replace('\\:', ':')
+                except ValueError:
+                    value, label = line, line
+                data.append((value, label))
+            return data
+
 
 
 class CustomLinkImportForm(CSVModelForm):
 class CustomLinkImportForm(CSVModelForm):
     content_types = CSVMultipleContentTypeField(
     content_types = CSVMultipleContentTypeField(

+ 14 - 0
netbox/extras/forms/model_forms.py

@@ -104,11 +104,25 @@ class CustomFieldChoiceSetForm(BootstrapMixin, forms.ModelForm):
         model = CustomFieldChoiceSet
         model = CustomFieldChoiceSet
         fields = ('name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically')
         fields = ('name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically')
 
 
+    def __init__(self, *args, initial=None, **kwargs):
+        super().__init__(*args, initial=initial, **kwargs)
+
+        # Escape colons in extra_choices
+        if 'extra_choices' in self.initial and self.initial['extra_choices']:
+            choices = []
+            for choice in self.initial['extra_choices']:
+                choice = (choice[0].replace(':', '\\:'), choice[1].replace(':', '\\:'))
+                choices.append(choice)
+
+            self.initial['extra_choices'] = choices
+
     def clean_extra_choices(self):
     def clean_extra_choices(self):
         data = []
         data = []
         for line in self.cleaned_data['extra_choices'].splitlines():
         for line in self.cleaned_data['extra_choices'].splitlines():
             try:
             try:
                 value, label = re.split(r'(?<!\\):', line, maxsplit=1)
                 value, label = re.split(r'(?<!\\):', line, maxsplit=1)
+                value = value.replace('\\:', ':')
+                label = label.replace('\\:', ':')
             except ValueError:
             except ValueError:
                 value, label = line, line
                 value, label = line, line
             data.append((value, label))
             data.append((value, label))

+ 9 - 3
netbox/extras/tests/test_views.py

@@ -93,6 +93,10 @@ class CustomFieldChoiceSetTestCase(ViewTestCases.PrimaryObjectViewTestCase):
                 name='Choice Set 3',
                 name='Choice Set 3',
                 extra_choices=(('C1', 'Choice 1'), ('C2', 'Choice 2'), ('C3', 'Choice 3'))
                 extra_choices=(('C1', 'Choice 1'), ('C2', 'Choice 2'), ('C3', 'Choice 3'))
             ),
             ),
+            CustomFieldChoiceSet(
+                name='Choice Set 4',
+                extra_choices=(('D1', 'Choice 1'), ('D2', 'Choice 2'), ('D3', 'Choice 3'))
+            ),
         )
         )
         CustomFieldChoiceSet.objects.bulk_create(choice_sets)
         CustomFieldChoiceSet.objects.bulk_create(choice_sets)
 
 
@@ -103,9 +107,10 @@ class CustomFieldChoiceSetTestCase(ViewTestCases.PrimaryObjectViewTestCase):
 
 
         cls.csv_data = (
         cls.csv_data = (
             'name,extra_choices',
             'name,extra_choices',
-            'Choice Set 4,"D1,D2,D3"',
-            'Choice Set 5,"E1,E2,E3"',
-            'Choice Set 6,"F1,F2,F3"',
+            'Choice Set 5,"D1,D2,D3"',
+            'Choice Set 6,"E1,E2,E3"',
+            'Choice Set 7,"F1,F2,F3"',
+            'Choice Set 8,"F1:L1,F2:L2,F3:L3"',
         )
         )
 
 
         cls.csv_update_data = (
         cls.csv_update_data = (
@@ -113,6 +118,7 @@ class CustomFieldChoiceSetTestCase(ViewTestCases.PrimaryObjectViewTestCase):
             f'{choice_sets[0].pk},"A,B,C"',
             f'{choice_sets[0].pk},"A,B,C"',
             f'{choice_sets[1].pk},"A,B,C"',
             f'{choice_sets[1].pk},"A,B,C"',
             f'{choice_sets[2].pk},"A,B,C"',
             f'{choice_sets[2].pk},"A,B,C"',
+            f'{choice_sets[3].pk},"A:L1,B:L2,C:L3"',
         )
         )
 
 
         cls.bulk_edit_data = {
         cls.bulk_edit_data = {