mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-25 18:08:38 -06:00
Closes #5003: CSV import now accepts slug values for choice fields
This commit is contained in:
parent
70ec5b9f37
commit
0cc2a6b2cf
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
* [#1692](https://github.com/netbox-community/netbox/issues/1692) - Allow assigment of inventory items to parent items in web UI
|
* [#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
|
* [#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
|
* [#5146](https://github.com/netbox-community/netbox/issues/5146) - Add custom fields support for cables, power panels, rack reservations, and virtual chassis
|
||||||
|
|
||||||
### Other Changes
|
### Other Changes
|
||||||
|
@ -983,9 +983,9 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
"device_role,manufacturer,device_type,status,name,site,rack_group,rack,position,face",
|
"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 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 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 6,Site 1,Rack Group 1,Rack 1,30,front",
|
||||||
)
|
)
|
||||||
|
|
||||||
cls.bulk_edit_data = {
|
cls.bulk_edit_data = {
|
||||||
@ -1267,9 +1267,9 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
|||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
"device,name,type",
|
"device,name,type",
|
||||||
"Device 1,Interface 4,1000BASE-T (1GE)",
|
"Device 1,Interface 4,1000base-t",
|
||||||
"Device 1,Interface 5,1000BASE-T (1GE)",
|
"Device 1,Interface 5,1000base-t",
|
||||||
"Device 1,Interface 6,1000BASE-T (1GE)",
|
"Device 1,Interface 6,1000base-t",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -1326,9 +1326,9 @@ class FrontPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
|||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
"device,name,type,rear_port,rear_port_position",
|
"device,name,type,rear_port,rear_port_position",
|
||||||
"Device 1,Front Port 4,8P8C,Rear Port 4,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 5,8p8c,Rear Port 5,1",
|
||||||
"Device 1,Front Port 6,8P8C,Rear Port 6,1",
|
"Device 1,Front Port 6,8p8c,Rear Port 6,1",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -1372,9 +1372,9 @@ class RearPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
|||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
"device,name,type,positions",
|
"device,name,type,positions",
|
||||||
"Device 1,Rear Port 4,8P8C,1",
|
"Device 1,Rear Port 4,8p8c,1",
|
||||||
"Device 1,Rear Port 5,8P8C,1",
|
"Device 1,Rear Port 5,8p8c,1",
|
||||||
"Device 1,Rear Port 6,8P8C,1",
|
"Device 1,Rear Port 6,8p8c,1",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -194,9 +194,9 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
"vrf,prefix,status",
|
"vrf,prefix,status",
|
||||||
"VRF 1,10.4.0.0/16,Active",
|
"VRF 1,10.4.0.0/16,active",
|
||||||
"VRF 1,10.5.0.0/16,Active",
|
"VRF 1,10.5.0.0/16,active",
|
||||||
"VRF 1,10.6.0.0/16,Active",
|
"VRF 1,10.6.0.0/16,active",
|
||||||
)
|
)
|
||||||
|
|
||||||
cls.bulk_edit_data = {
|
cls.bulk_edit_data = {
|
||||||
@ -244,9 +244,9 @@ class IPAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
"vrf,address,status",
|
"vrf,address,status",
|
||||||
"VRF 1,192.0.2.4/24,Active",
|
"VRF 1,192.0.2.4/24,active",
|
||||||
"VRF 1,192.0.2.5/24,Active",
|
"VRF 1,192.0.2.5/24,active",
|
||||||
"VRF 1,192.0.2.6/24,Active",
|
"VRF 1,192.0.2.6/24,active",
|
||||||
)
|
)
|
||||||
|
|
||||||
cls.bulk_edit_data = {
|
cls.bulk_edit_data = {
|
||||||
@ -334,9 +334,9 @@ class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
"vid,name,status",
|
"vid,name,status",
|
||||||
"104,VLAN104,Active",
|
"104,VLAN104,active",
|
||||||
"105,VLAN105,Active",
|
"105,VLAN105,active",
|
||||||
"106,VLAN106,Active",
|
"106,VLAN106,active",
|
||||||
)
|
)
|
||||||
|
|
||||||
cls.bulk_edit_data = {
|
cls.bulk_edit_data = {
|
||||||
@ -393,9 +393,9 @@ class ServiceTestCase(
|
|||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
"device,name,protocol,port,description",
|
"device,name,protocol,port,description",
|
||||||
"Device 1,Service 1,TCP,1,First service",
|
"Device 1,Service 1,tcp,1,First service",
|
||||||
"Device 1,Service 2,TCP,2,Second service",
|
"Device 1,Service 2,tcp,2,Second service",
|
||||||
"Device 1,Service 3,UDP,3,Third service",
|
"Device 1,Service 3,udp,3,Third service",
|
||||||
)
|
)
|
||||||
|
|
||||||
cls.bulk_edit_data = {
|
cls.bulk_edit_data = {
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<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">
|
<button type="button" class="btn btn-primary btn-xs pull-right" data-toggle="modal" data-target="#{{ name }}_choices">
|
||||||
<i class="fa fa-question"></i>
|
<i class="fa fa-question"></i>
|
||||||
</button>
|
</button>
|
||||||
@ -77,9 +77,12 @@
|
|||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
<h4 class="modal-title"><code>{{ name }}</code> Choices</h4>
|
<h4 class="modal-title"><code>{{ name }}</code> Choices</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<table class="table table-striped modal-body">
|
||||||
<ul>{% for value, label in field.choices %}{% if value %}<li>{{ value }}</li>{% endif %}{% endfor %}</ul>
|
<tr><th>Import Value</th><th>Label</th></tr>
|
||||||
</div>
|
{% for value, label in field.choices %}
|
||||||
|
{% if value %}<tr><td><samp>{{ value }}</samp></td><td>{{ label }}</td></tr>{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -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.
|
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):
|
STATIC_CHOICES = True
|
||||||
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)}
|
|
||||||
|
|
||||||
def clean(self, value):
|
def __init__(self, *, choices=(), **kwargs):
|
||||||
value = super().clean(value)
|
super().__init__(choices=choices, **kwargs)
|
||||||
if not value:
|
self.choices = unpack_grouped_choices(choices)
|
||||||
return ''
|
|
||||||
if value not in self.choice_values:
|
|
||||||
raise forms.ValidationError("Invalid choice: {}".format(value))
|
|
||||||
return self.choice_values[value]
|
|
||||||
|
|
||||||
|
|
||||||
class CSVModelChoiceField(forms.ModelChoiceField):
|
class CSVModelChoiceField(forms.ModelChoiceField):
|
||||||
|
Loading…
Reference in New Issue
Block a user