Skip to content

Commit ae4ea34

Browse files
Fixes #11617: Check for invalid CSV headers during bulk import (#13826)
* Fixes #11617: Check for invalid CSV headers during bulk import * Add test for CSV import header validation
1 parent f5dd7d8 commit ae4ea34

File tree

3 files changed

+43
-10
lines changed

3 files changed

+43
-10
lines changed

netbox/netbox/tests/test_import.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,36 @@ def setUpTestData(cls):
1717
def _get_csv_data(self, csv_data):
1818
return '\n'.join(csv_data)
1919

20+
def test_invalid_headers(self):
21+
"""
22+
Test that import form validation fails when an unknown CSV header is present.
23+
"""
24+
self.add_permissions('dcim.add_region')
25+
26+
csv_data = [
27+
'name,slug,INVALIDHEADER',
28+
'Region 1,region-1,abc',
29+
'Region 2,region-2,def',
30+
'Region 3,region-3,ghi',
31+
]
32+
data = {
33+
'format': ImportFormatChoices.CSV,
34+
'data': self._get_csv_data(csv_data),
35+
'csv_delimiter': CSVDelimiterChoices.AUTO,
36+
}
37+
38+
# Form validation should fail with invalid header present
39+
self.assertHttpStatus(self.client.post(self._get_url('import'), data), 200)
40+
self.assertEqual(Region.objects.count(), 0)
41+
42+
# Correct the CSV header name
43+
csv_data[0] = 'name,slug,description'
44+
data['data'] = self._get_csv_data(csv_data)
45+
46+
# Validation should succeed
47+
self.assertHttpStatus(self.client.post(self._get_url('import'), data), 302)
48+
self.assertEqual(Region.objects.count(), 3)
49+
2050
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
2151
def test_valid_tags(self):
2252
csv_data = (

netbox/utilities/forms/bulk_import.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ def _clean_csv(self, data, delimiter=CSVDelimiterChoices.AUTO):
129129
headers, records = parse_csv(reader)
130130

131131
# Set CSV headers for reference by the model form
132+
headers.pop('id', None)
132133
self._csv_headers = headers
133134

134135
return records

netbox/utilities/forms/forms.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,22 +70,24 @@ class CSVModelForm(forms.ModelForm):
7070
"""
7171
ModelForm used for the import of objects in CSV format.
7272
"""
73-
def __init__(self, *args, headers=None, fields=None, **kwargs):
74-
headers = headers or {}
75-
fields = fields or []
73+
def __init__(self, *args, headers=None, **kwargs):
74+
self.headers = headers or {}
7675
super().__init__(*args, **kwargs)
7776

7877
# Modify the model form to accommodate any customized to_field_name properties
79-
for field, to_field in headers.items():
78+
for field, to_field in self.headers.items():
8079
if to_field is not None:
8180
self.fields[field].to_field_name = to_field
8281

83-
# Omit any fields not specified (e.g. because the form is being used to
84-
# updated rather than create objects)
85-
if fields:
86-
for field in list(self.fields.keys()):
87-
if field not in fields:
88-
del self.fields[field]
82+
def clean(self):
83+
# Flag any invalid CSV headers
84+
for header in self.headers:
85+
if header not in self.fields:
86+
raise forms.ValidationError(
87+
_("Unrecognized header: {name}").format(name=header)
88+
)
89+
90+
return super().clean()
8991

9092

9193
class FilterForm(BootstrapMixin, forms.Form):

0 commit comments

Comments
 (0)