Просмотр исходного кода

Closes #5003: CSV import now accepts slug values for choice fields

Jeremy Stretch 5 лет назад
Родитель
Сommit
0cc2a6b2cf

+ 1 - 0
docs/release-notes/version-2.10.md

@@ -8,6 +8,7 @@
 
 * [#1692](https://github.com/netbox-community/netbox/issues/1692) - Allow assigment of inventory items to parent items in web UI
 * [#4956](https://github.com/netbox-community/netbox/issues/4956) - Include inventory items on primary device view
+* [#5003](https://github.com/netbox-community/netbox/issues/5003) - CSV import now accepts slug values for choice fields
 * [#5146](https://github.com/netbox-community/netbox/issues/5146) - Add custom fields support for cables, power panels, rack reservations, and virtual chassis
 
 ### Other Changes

+ 12 - 12
netbox/dcim/tests/test_views.py

@@ -983,9 +983,9 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
 
         cls.csv_data = (
             "device_role,manufacturer,device_type,status,name,site,rack_group,rack,position,face",
-            "Device Role 1,Manufacturer 1,Device Type 1,Active,Device 4,Site 1,Rack Group 1,Rack 1,10,Front",
-            "Device Role 1,Manufacturer 1,Device Type 1,Active,Device 5,Site 1,Rack Group 1,Rack 1,20,Front",
-            "Device Role 1,Manufacturer 1,Device Type 1,Active,Device 6,Site 1,Rack Group 1,Rack 1,30,Front",
+            "Device Role 1,Manufacturer 1,Device Type 1,active,Device 4,Site 1,Rack Group 1,Rack 1,10,front",
+            "Device Role 1,Manufacturer 1,Device Type 1,active,Device 5,Site 1,Rack Group 1,Rack 1,20,front",
+            "Device Role 1,Manufacturer 1,Device Type 1,active,Device 6,Site 1,Rack Group 1,Rack 1,30,front",
         )
 
         cls.bulk_edit_data = {
@@ -1267,9 +1267,9 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
 
         cls.csv_data = (
             "device,name,type",
-            "Device 1,Interface 4,1000BASE-T (1GE)",
-            "Device 1,Interface 5,1000BASE-T (1GE)",
-            "Device 1,Interface 6,1000BASE-T (1GE)",
+            "Device 1,Interface 4,1000base-t",
+            "Device 1,Interface 5,1000base-t",
+            "Device 1,Interface 6,1000base-t",
         )
 
 
@@ -1326,9 +1326,9 @@ class FrontPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
 
         cls.csv_data = (
             "device,name,type,rear_port,rear_port_position",
-            "Device 1,Front Port 4,8P8C,Rear Port 4,1",
-            "Device 1,Front Port 5,8P8C,Rear Port 5,1",
-            "Device 1,Front Port 6,8P8C,Rear Port 6,1",
+            "Device 1,Front Port 4,8p8c,Rear Port 4,1",
+            "Device 1,Front Port 5,8p8c,Rear Port 5,1",
+            "Device 1,Front Port 6,8p8c,Rear Port 6,1",
         )
 
 
@@ -1372,9 +1372,9 @@ class RearPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
 
         cls.csv_data = (
             "device,name,type,positions",
-            "Device 1,Rear Port 4,8P8C,1",
-            "Device 1,Rear Port 5,8P8C,1",
-            "Device 1,Rear Port 6,8P8C,1",
+            "Device 1,Rear Port 4,8p8c,1",
+            "Device 1,Rear Port 5,8p8c,1",
+            "Device 1,Rear Port 6,8p8c,1",
         )
 
 

+ 12 - 12
netbox/ipam/tests/test_views.py

@@ -194,9 +194,9 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
 
         cls.csv_data = (
             "vrf,prefix,status",
-            "VRF 1,10.4.0.0/16,Active",
-            "VRF 1,10.5.0.0/16,Active",
-            "VRF 1,10.6.0.0/16,Active",
+            "VRF 1,10.4.0.0/16,active",
+            "VRF 1,10.5.0.0/16,active",
+            "VRF 1,10.6.0.0/16,active",
         )
 
         cls.bulk_edit_data = {
@@ -244,9 +244,9 @@ class IPAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase):
 
         cls.csv_data = (
             "vrf,address,status",
-            "VRF 1,192.0.2.4/24,Active",
-            "VRF 1,192.0.2.5/24,Active",
-            "VRF 1,192.0.2.6/24,Active",
+            "VRF 1,192.0.2.4/24,active",
+            "VRF 1,192.0.2.5/24,active",
+            "VRF 1,192.0.2.6/24,active",
         )
 
         cls.bulk_edit_data = {
@@ -334,9 +334,9 @@ class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
 
         cls.csv_data = (
             "vid,name,status",
-            "104,VLAN104,Active",
-            "105,VLAN105,Active",
-            "106,VLAN106,Active",
+            "104,VLAN104,active",
+            "105,VLAN105,active",
+            "106,VLAN106,active",
         )
 
         cls.bulk_edit_data = {
@@ -393,9 +393,9 @@ class ServiceTestCase(
 
         cls.csv_data = (
             "device,name,protocol,port,description",
-            "Device 1,Service 1,TCP,1,First service",
-            "Device 1,Service 2,TCP,2,Second service",
-            "Device 1,Service 3,UDP,3,Third service",
+            "Device 1,Service 1,tcp,1,First service",
+            "Device 1,Service 2,tcp,2,Second service",
+            "Device 1,Service 3,udp,3,Third service",
         )
 
         cls.bulk_edit_data = {

+ 7 - 4
netbox/templates/utilities/obj_bulk_import.html

@@ -66,7 +66,7 @@
                                             {% endif %}
                                         </td>
                                         <td>
-                                            {% if field.choice_values %}
+                                            {% if field.STATIC_CHOICES %}
                                                 <button type="button" class="btn btn-primary btn-xs pull-right" data-toggle="modal" data-target="#{{ name }}_choices">
                                                     <i class="fa fa-question"></i>
                                                 </button>
@@ -77,9 +77,12 @@
                                                                 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                                                                 <h4 class="modal-title"><code>{{ name }}</code> Choices</h4>
                                                             </div>
-                                                            <div class="modal-body">
-                                                                <ul>{% for value, label in field.choices %}{% if value %}<li>{{ value }}</li>{% endif %}{% endfor %}</ul>
-                                                            </div>
+                                                            <table class="table table-striped modal-body">
+                                                                <tr><th>Import Value</th><th>Label</th></tr>
+                                                                {% for value, label in field.choices %}
+                                                                    {% if value %}<tr><td><samp>{{ value }}</samp></td><td>{{ label }}</td></tr>{% endif %}
+                                                                {% endfor %}
+                                                            </table>
                                                         </div>
                                                     </div>
                                                 </div>

+ 4 - 11
netbox/utilities/forms/fields.py

@@ -117,18 +117,11 @@ class CSVChoiceField(forms.ChoiceField):
     """
     Invert the provided set of choices to take the human-friendly label as input, and return the database value.
     """
-    def __init__(self, choices, *args, **kwargs):
-        super().__init__(choices=choices, *args, **kwargs)
-        self.choices = [(label, label) for value, label in unpack_grouped_choices(choices)]
-        self.choice_values = {label: value for value, label in unpack_grouped_choices(choices)}
+    STATIC_CHOICES = True
 
-    def clean(self, value):
-        value = super().clean(value)
-        if not value:
-            return ''
-        if value not in self.choice_values:
-            raise forms.ValidationError("Invalid choice: {}".format(value))
-        return self.choice_values[value]
+    def __init__(self, *, choices=(), **kwargs):
+        super().__init__(choices=choices, **kwargs)
+        self.choices = unpack_grouped_choices(choices)
 
 
 class CSVModelChoiceField(forms.ModelChoiceField):