From 55b7cf21ccc834598bdb9d1a4bfd6ac0d3bc80fd Mon Sep 17 00:00:00 2001 From: Alyssa Bigley Date: Thu, 10 Jun 2021 14:41:33 -0400 Subject: [PATCH] changed name of csv_file variable and started work on ValidationError --- netbox/netbox/views/generic.py | 12 +++++++--- netbox/utilities/forms/fields.py | 38 ++++++++++++++++++-------------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/netbox/netbox/views/generic.py b/netbox/netbox/views/generic.py index 24d459219..7ff980750 100644 --- a/netbox/netbox/views/generic.py +++ b/netbox/netbox/views/generic.py @@ -665,10 +665,16 @@ class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): from_form=self.model_form, widget=Textarea(attrs=self.widget_attrs) ) - upload_csv = CSVFileField( + csv_file = CSVFileField( + label="CSV file", from_form=self.model_form, required=False ) + def used_both_methods(self): + if self.cleaned_data['csv_file'][1] and self.cleaned_data['csv'][1]: + raise ValidationError('') + return False + return ImportForm(*args, **kwargs) def _save_obj(self, obj_form, request): @@ -694,14 +700,14 @@ class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): new_objs = [] form = self._import_form(request.POST, request.FILES) - if form.is_valid(): + if form.is_valid() and not form.used_both_methods(): logger.debug("Form validation was successful") try: # Iterate through CSV data and bind each row to a new model form instance. with transaction.atomic(): if request.FILES: - headers, records = form.cleaned_data['upload_csv'] + headers, records = form.cleaned_data['csv_file'] else: headers, records = form.cleaned_data['csv'] for row, data in enumerate(records, start=1): diff --git a/netbox/utilities/forms/fields.py b/netbox/utilities/forms/fields.py index 0f5b0b6dd..2954f9c73 100644 --- a/netbox/utilities/forms/fields.py +++ b/netbox/utilities/forms/fields.py @@ -246,35 +246,39 @@ class CSVFileField(forms.FileField): def to_python(self, file): records = [] - csv_str = file.read().decode('utf-8') - reader = csv.reader(csv_str.splitlines()) + if file: + csv_str = file.read().decode('utf-8') + reader = csv.reader(csv_str.splitlines()) # Consume the first line of CSV data as column headers. Create a dictionary mapping each header to an optional # "to" field specifying how the related object is being referenced. For example, importing a Device might use a # `site.slug` header, to indicate the related site is being referenced by its slug. headers = {} - for header in next(reader): - if '.' in header: - field, to_field = header.split('.', 1) - headers[field] = to_field - else: - headers[header] = None + if file: + for header in next(reader): + if '.' in header: + field, to_field = header.split('.', 1) + headers[field] = to_field + else: + headers[header] = None - # Parse CSV rows into a list of dictionaries mapped from the column headers. - for i, row in enumerate(reader, start=1): - if len(row) != len(headers): - raise forms.ValidationError( - f"Row {i}: Expected {len(headers)} columns but found {len(row)}" - ) - row = [col.strip() for col in row] - record = dict(zip(headers.keys(), row)) - records.append(record) + # Parse CSV rows into a list of dictionaries mapped from the column headers. + for i, row in enumerate(reader, start=1): + if len(row) != len(headers): + raise forms.ValidationError( + f"Row {i}: Expected {len(headers)} columns but found {len(row)}" + ) + row = [col.strip() for col in row] + record = dict(zip(headers.keys(), row)) + records.append(record) return headers, records def validate(self, value): headers, records = value + if not headers and not records: + return value # Validate provided column headers for field, to_field in headers.items():