Changelog and cleanup for #6560

This commit is contained in:
jeremystretch 2021-07-28 11:44:13 -04:00
parent ea0de4b01d
commit 8d9d3a9e7d
5 changed files with 122 additions and 112 deletions

View File

@ -4,6 +4,8 @@
### Enhancements ### Enhancements
* [#6560](https://github.com/netbox-community/netbox/issues/6560) - Enable CSV import via uploaded file
* [#6771](https://github.com/netbox-community/netbox/issues/6771) - Add count of inventory items to manufacturer view
* [#6785](https://github.com/netbox-community/netbox/issues/6785) - Add "hardwired" type for power port types * [#6785](https://github.com/netbox-community/netbox/issues/6785) - Add "hardwired" type for power port types
### Bug Fixes ### Bug Fixes
@ -22,7 +24,6 @@
### Other Changes ### Other Changes
* [#6771](https://github.com/netbox-community/netbox/issues/6771) - Add count of inventory items to manufacturer view
* [#6781](https://github.com/netbox-community/netbox/issues/6781) - Database query caching is now disabled by default * [#6781](https://github.com/netbox-community/netbox/issues/6781) - Database query caching is now disabled by default
--- ---

View File

@ -20,7 +20,8 @@ from extras.models import CustomField, ExportTemplate
from utilities.error_handlers import handle_protectederror from utilities.error_handlers import handle_protectederror
from utilities.exceptions import AbortTransaction, PermissionsViolation from utilities.exceptions import AbortTransaction, PermissionsViolation
from utilities.forms import ( from utilities.forms import (
BootstrapMixin, BulkRenameForm, ConfirmationForm, CSVDataField, ImportForm, TableConfigForm, restrict_form_fields, CSVFileField BootstrapMixin, BulkRenameForm, ConfirmationForm, CSVDataField, CSVFileField, ImportForm, TableConfigForm,
restrict_form_fields,
) )
from utilities.permissions import get_permission_for_model from utilities.permissions import get_permission_for_model
from utilities.tables import paginate_table from utilities.tables import paginate_table
@ -673,8 +674,16 @@ class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
required=False required=False
) )
def used_both_csv_fields(self): def clean(self):
return self.cleaned_data['csv_file'][1] and self.cleaned_data['csv'][1] csv_rows = self.cleaned_data['csv'][1]
csv_file = self.files.get('csv_file')
# Check that the user has not submitted both text data and a file
if csv_rows and csv_file:
raise ValidationError(
"Cannot process CSV text and file attachment simultaneously. Please choose only one import "
"method."
)
return ImportForm(*args, **kwargs) return ImportForm(*args, **kwargs)
@ -705,9 +714,6 @@ class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
logger.debug("Form validation was successful") logger.debug("Form validation was successful")
try: try:
if form.used_both_csv_fields():
form.add_error('csv_file', "Choose one of two import methods")
raise ValidationError("")
# Iterate through CSV data and bind each row to a new model form instance. # Iterate through CSV data and bind each row to a new model form instance.
with transaction.atomic(): with transaction.atomic():
if request.FILES: if request.FILES:

View File

@ -16,13 +16,19 @@
</div> </div>
{% endif %} {% endif %}
<ul class="nav nav-tabs" role="tablist"> <ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#csv" role="tab" data-toggle="tab">CSV</a></li> <li role="presentation" class="active"><a href="#csv" role="tab" data-toggle="tab">CSV Data</a></li>
<li role="presentation"><a href="#csv-file" role="tab" data-toggle="tab">CSV File Upload</a></li>
</ul> </ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="csv">
<form action="" method="post" class="form" enctype="multipart/form-data"> <form action="" method="post" class="form" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
{% render_form form %} <div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="csv">
{% render_field form.csv %}
</div>
<div role="tabpanel" class="tab-pane" id="csv-file">
{% render_field form.csv_file %}
</div>
</div>
<div class="form-group"> <div class="form-group">
<div class="col-md-12 text-right"> <div class="col-md-12 text-right">
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary">Submit</button>
@ -113,6 +119,4 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -208,22 +208,20 @@ class CSVFileField(forms.FileField):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def to_python(self, file): def to_python(self, file):
if file: if file is None:
csv_str = file.read().decode('utf-8') return None
reader = csv.reader(csv_str.splitlines())
headers = {} csv_str = file.read().decode('utf-8').strip()
records = [] reader = csv.reader(csv_str.splitlines())
if file:
headers, records = parse_csv(reader) headers, records = parse_csv(reader)
return headers, records return headers, records
def validate(self, value): def validate(self, value):
headers, records = value if value is None:
if not headers and not records: return None
return value
headers, records = value
validate_csv(headers, self.fields, self.required_fields) validate_csv(headers, self.fields, self.required_fields)
return value return value

View File

@ -166,6 +166,7 @@ def parse_csv(reader):
row = [col.strip() for col in row] row = [col.strip() for col in row]
record = dict(zip(headers.keys(), row)) record = dict(zip(headers.keys(), row))
records.append(record) records.append(record)
return headers, records return headers, records