mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
* Fixes #11617: Check for invalid CSV headers during bulk import * Add test for CSV import header validation
This commit is contained in:
@ -17,6 +17,36 @@ class CSVImportTestCase(ModelViewTestCase):
|
|||||||
def _get_csv_data(self, csv_data):
|
def _get_csv_data(self, csv_data):
|
||||||
return '\n'.join(csv_data)
|
return '\n'.join(csv_data)
|
||||||
|
|
||||||
|
def test_invalid_headers(self):
|
||||||
|
"""
|
||||||
|
Test that import form validation fails when an unknown CSV header is present.
|
||||||
|
"""
|
||||||
|
self.add_permissions('dcim.add_region')
|
||||||
|
|
||||||
|
csv_data = [
|
||||||
|
'name,slug,INVALIDHEADER',
|
||||||
|
'Region 1,region-1,abc',
|
||||||
|
'Region 2,region-2,def',
|
||||||
|
'Region 3,region-3,ghi',
|
||||||
|
]
|
||||||
|
data = {
|
||||||
|
'format': ImportFormatChoices.CSV,
|
||||||
|
'data': self._get_csv_data(csv_data),
|
||||||
|
'csv_delimiter': CSVDelimiterChoices.AUTO,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Form validation should fail with invalid header present
|
||||||
|
self.assertHttpStatus(self.client.post(self._get_url('import'), data), 200)
|
||||||
|
self.assertEqual(Region.objects.count(), 0)
|
||||||
|
|
||||||
|
# Correct the CSV header name
|
||||||
|
csv_data[0] = 'name,slug,description'
|
||||||
|
data['data'] = self._get_csv_data(csv_data)
|
||||||
|
|
||||||
|
# Validation should succeed
|
||||||
|
self.assertHttpStatus(self.client.post(self._get_url('import'), data), 302)
|
||||||
|
self.assertEqual(Region.objects.count(), 3)
|
||||||
|
|
||||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
def test_valid_tags(self):
|
def test_valid_tags(self):
|
||||||
csv_data = (
|
csv_data = (
|
||||||
|
@ -129,6 +129,7 @@ class BulkImportForm(BootstrapMixin, SyncedDataMixin, forms.Form):
|
|||||||
headers, records = parse_csv(reader)
|
headers, records = parse_csv(reader)
|
||||||
|
|
||||||
# Set CSV headers for reference by the model form
|
# Set CSV headers for reference by the model form
|
||||||
|
headers.pop('id', None)
|
||||||
self._csv_headers = headers
|
self._csv_headers = headers
|
||||||
|
|
||||||
return records
|
return records
|
||||||
|
@ -70,22 +70,24 @@ class CSVModelForm(forms.ModelForm):
|
|||||||
"""
|
"""
|
||||||
ModelForm used for the import of objects in CSV format.
|
ModelForm used for the import of objects in CSV format.
|
||||||
"""
|
"""
|
||||||
def __init__(self, *args, headers=None, fields=None, **kwargs):
|
def __init__(self, *args, headers=None, **kwargs):
|
||||||
headers = headers or {}
|
self.headers = headers or {}
|
||||||
fields = fields or []
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
# Modify the model form to accommodate any customized to_field_name properties
|
# Modify the model form to accommodate any customized to_field_name properties
|
||||||
for field, to_field in headers.items():
|
for field, to_field in self.headers.items():
|
||||||
if to_field is not None:
|
if to_field is not None:
|
||||||
self.fields[field].to_field_name = to_field
|
self.fields[field].to_field_name = to_field
|
||||||
|
|
||||||
# Omit any fields not specified (e.g. because the form is being used to
|
def clean(self):
|
||||||
# updated rather than create objects)
|
# Flag any invalid CSV headers
|
||||||
if fields:
|
for header in self.headers:
|
||||||
for field in list(self.fields.keys()):
|
if header not in self.fields:
|
||||||
if field not in fields:
|
raise forms.ValidationError(
|
||||||
del self.fields[field]
|
_("Unrecognized header: {name}").format(name=header)
|
||||||
|
)
|
||||||
|
|
||||||
|
return super().clean()
|
||||||
|
|
||||||
|
|
||||||
class FilterForm(BootstrapMixin, forms.Form):
|
class FilterForm(BootstrapMixin, forms.Form):
|
||||||
|
Reference in New Issue
Block a user