mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-23 17:08:41 -06:00
11955 remove csvdatafield csvfilefield
This commit is contained in:
parent
43bba935c3
commit
5b81986bb3
@ -14,8 +14,6 @@ from utilities.utils import content_type_identifier
|
|||||||
__all__ = (
|
__all__ = (
|
||||||
'CSVChoiceField',
|
'CSVChoiceField',
|
||||||
'CSVContentTypeField',
|
'CSVContentTypeField',
|
||||||
'CSVDataField',
|
|
||||||
'CSVFileField',
|
|
||||||
'CSVModelChoiceField',
|
'CSVModelChoiceField',
|
||||||
'CSVModelMultipleChoiceField',
|
'CSVModelMultipleChoiceField',
|
||||||
'CSVMultipleChoiceField',
|
'CSVMultipleChoiceField',
|
||||||
@ -24,90 +22,6 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
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:
|
class CSVChoicesMixin:
|
||||||
STATIC_CHOICES = True
|
STATIC_CHOICES = True
|
||||||
|
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from ipam.forms import IPAddressImportForm
|
|
||||||
from utilities.choices import ImportFormatChoices
|
from utilities.choices import ImportFormatChoices
|
||||||
from utilities.forms.bulk_import import ImportForm
|
from utilities.forms.bulk_import import ImportForm
|
||||||
from utilities.forms.fields import CSVDataField
|
|
||||||
from utilities.forms.utils import expand_alphanumeric_pattern, expand_ipaddress_pattern
|
from utilities.forms.utils import expand_alphanumeric_pattern, expand_ipaddress_pattern
|
||||||
|
|
||||||
|
|
||||||
@ -287,88 +285,6 @@ class ExpandAlphanumeric(TestCase):
|
|||||||
sorted(expand_alphanumeric_pattern('r[a,,b]a'))
|
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):
|
class ImportFormTest(TestCase):
|
||||||
|
|
||||||
def test_format_detection(self):
|
def test_format_detection(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user