diff --git a/netbox/utilities/forms/fields/csv.py b/netbox/utilities/forms/fields/csv.py index f964d0db010..f89f5f2ef73 100644 --- a/netbox/utilities/forms/fields/csv.py +++ b/netbox/utilities/forms/fields/csv.py @@ -14,8 +14,6 @@ __all__ = ( 'CSVChoiceField', 'CSVContentTypeField', - 'CSVDataField', - 'CSVFileField', 'CSVModelChoiceField', 'CSVModelMultipleChoiceField', 'CSVMultipleChoiceField', @@ -24,90 +22,6 @@ ) -class CSVDataField(forms.CharField): - """ - A CharField (rendered as a Textarea) which accepts CSV-formatted data. It returns data as a two-tuple: The first - item is a dictionary of column headers, mapping field names to the attribute by which they match a related object - (where applicable). The second item is a list of dictionaries, each representing a discrete row of CSV data. - - :param from_form: The form from which the field derives its validation rules. - """ - widget = forms.Textarea - - def __init__(self, from_form, *args, **kwargs): - - form = from_form() - self.model = form.Meta.model - self.fields = form.fields - self.required_fields = [ - name for name, field in form.fields.items() if field.required - ] - - super().__init__(*args, **kwargs) - - self.strip = False - if not self.label: - self.label = '' - if not self.initial: - self.initial = ','.join(self.required_fields) + '\n' - if not self.help_text: - self.help_text = _('Enter the list of column headers followed by one line per record to be imported, using ' - 'commas to separate values. Multi-line data and values containing commas may be wrapped ' - 'in double quotes.') - - def to_python(self, value): - reader = csv.reader(StringIO(value.strip())) - - return parse_csv(reader) - - def validate(self, value): - headers, records = value - validate_csv(headers, self.fields, self.required_fields) - - return value - - -class CSVFileField(forms.FileField): - """ - A FileField (rendered as a file input button) which accepts a file containing CSV-formatted data. It returns - data as a two-tuple: The first item is a dictionary of column headers, mapping field names to the attribute - by which they match a related object (where applicable). The second item is a list of dictionaries, each - representing a discrete row of CSV data. - - :param from_form: The form from which the field derives its validation rules. - """ - - def __init__(self, from_form, *args, **kwargs): - - form = from_form() - self.model = form.Meta.model - self.fields = form.fields - self.required_fields = [ - name for name, field in form.fields.items() if field.required - ] - - super().__init__(*args, **kwargs) - - def to_python(self, file): - if file is None: - return None - - csv_str = file.read().decode('utf-8').strip() - reader = csv.reader(StringIO(csv_str)) - headers, records = parse_csv(reader) - - return headers, records - - def validate(self, value): - if value is None: - return None - - headers, records = value - validate_csv(headers, self.fields, self.required_fields) - - return value - - class CSVChoicesMixin: STATIC_CHOICES = True diff --git a/netbox/utilities/tests/test_forms.py b/netbox/utilities/tests/test_forms.py index 99c98a406dd..d381ffc8012 100644 --- a/netbox/utilities/tests/test_forms.py +++ b/netbox/utilities/tests/test_forms.py @@ -1,10 +1,8 @@ from django import forms from django.test import TestCase -from ipam.forms import IPAddressImportForm from utilities.choices import ImportFormatChoices from utilities.forms.bulk_import import ImportForm -from utilities.forms.fields import CSVDataField from utilities.forms.utils import expand_alphanumeric_pattern, expand_ipaddress_pattern @@ -287,88 +285,6 @@ def test_invalid_set(self): sorted(expand_alphanumeric_pattern('r[a,,b]a')) -class CSVDataFieldTest(TestCase): - - def setUp(self): - self.field = CSVDataField(from_form=IPAddressImportForm) - - def test_clean(self): - input = """ - address,status,vrf - 192.0.2.1/32,Active,Test VRF - """ - output = ( - {'address': None, 'status': None, 'vrf': None}, - [{'address': '192.0.2.1/32', 'status': 'Active', 'vrf': 'Test VRF'}] - ) - self.assertEqual(self.field.clean(input), output) - - def test_clean_invalid_header(self): - input = """ - address,status,vrf,xxx - 192.0.2.1/32,Active,Test VRF,123 - """ - with self.assertRaises(forms.ValidationError): - self.field.clean(input) - - def test_clean_missing_required_header(self): - input = """ - status,vrf - Active,Test VRF - """ - with self.assertRaises(forms.ValidationError): - self.field.clean(input) - - def test_clean_default_to_field(self): - input = """ - address,status,vrf.name - 192.0.2.1/32,Active,Test VRF - """ - output = ( - {'address': None, 'status': None, 'vrf': 'name'}, - [{'address': '192.0.2.1/32', 'status': 'Active', 'vrf': 'Test VRF'}] - ) - self.assertEqual(self.field.clean(input), output) - - def test_clean_pk_to_field(self): - input = """ - address,status,vrf.pk - 192.0.2.1/32,Active,123 - """ - output = ( - {'address': None, 'status': None, 'vrf': 'pk'}, - [{'address': '192.0.2.1/32', 'status': 'Active', 'vrf': '123'}] - ) - self.assertEqual(self.field.clean(input), output) - - def test_clean_custom_to_field(self): - input = """ - address,status,vrf.rd - 192.0.2.1/32,Active,123:456 - """ - output = ( - {'address': None, 'status': None, 'vrf': 'rd'}, - [{'address': '192.0.2.1/32', 'status': 'Active', 'vrf': '123:456'}] - ) - self.assertEqual(self.field.clean(input), output) - - def test_clean_invalid_to_field(self): - input = """ - address,status,vrf.xxx - 192.0.2.1/32,Active,123:456 - """ - with self.assertRaises(forms.ValidationError): - self.field.clean(input) - - def test_clean_to_field_on_non_object(self): - input = """ - address,status.foo,vrf - 192.0.2.1/32,Bar,Test VRF - """ - with self.assertRaises(forms.ValidationError): - self.field.clean(input) - - class ImportFormTest(TestCase): def test_format_detection(self):