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

This commit is contained in:
Jeremy Stretch 2020-09-18 13:03:38 -04:00
parent 70ec5b9f37
commit 0cc2a6b2cf
5 changed files with 36 additions and 39 deletions

View File

@ -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

View File

@ -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",
)

View File

@ -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 = {

View File

@ -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>

View File

@ -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):