Browse Source

Fixes #1859: Implemented support for line breaks within CSV fields

Jeremy Stretch 7 years ago
parent
commit
60c03a646c
4 changed files with 64 additions and 69 deletions
  1. 0 61
      netbox/utilities/csv.py
  2. 3 7
      netbox/utilities/forms.py
  3. 60 0
      netbox/utilities/utils.py
  4. 1 1
      netbox/utilities/views.py

+ 0 - 61
netbox/utilities/csv.py

@@ -1,61 +0,0 @@
-from __future__ import unicode_literals
-
-import datetime
-import six
-
-from django.http import HttpResponse
-
-
-def csv_format(data):
-    """
-    Encapsulate any data which contains a comma within double quotes.
-    """
-    csv = []
-    for value in data:
-
-        # Represent None or False with empty string
-        if value in [None, False]:
-            csv.append('')
-            continue
-
-        # Convert dates to ISO format
-        if isinstance(value, (datetime.date, datetime.datetime)):
-            value = value.isoformat()
-
-        # Force conversion to string first so we can check for any commas
-        if not isinstance(value, six.string_types):
-            value = '{}'.format(value)
-
-        # Double-quote the value if it contains a comma
-        if ',' in value:
-            csv.append('"{}"'.format(value))
-        else:
-            csv.append('{}'.format(value))
-
-    return ','.join(csv)
-
-
-def queryset_to_csv(queryset):
-    """
-    Export a queryset of objects as CSV, using the model's to_csv() method.
-    """
-    output = []
-
-    # Start with the column headers
-    headers = ','.join(queryset.model.csv_headers)
-    output.append(headers)
-
-    # Iterate through the queryset
-    for obj in queryset:
-        data = csv_format(obj.to_csv())
-        output.append(data)
-
-    # Build the HTTP response
-    response = HttpResponse(
-        '\n'.join(output),
-        content_type='text/csv'
-    )
-    filename = 'netbox_{}.csv'.format(queryset.model._meta.verbose_name_plural)
-    response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
-
-    return response

+ 3 - 7
netbox/utilities/forms.py

@@ -1,7 +1,7 @@
 from __future__ import unicode_literals
 
 import csv
-import itertools
+from io import StringIO
 import re
 
 from django import forms
@@ -245,14 +245,10 @@ class CSVDataField(forms.CharField):
 
     def to_python(self, value):
 
-        # Python 2's csv module has problems with Unicode
-        if not isinstance(value, str):
-            value = value.encode('utf-8')
-
         records = []
-        reader = csv.reader(value.splitlines())
+        reader = csv.reader(StringIO(value))
 
-        # Consume and valdiate the first line of CSV data as column headers
+        # Consume and validate the first line of CSV data as column headers
         headers = next(reader)
         for f in self.required_fields:
             if f not in headers:

+ 60 - 0
netbox/utilities/utils.py

@@ -1,5 +1,65 @@
 from __future__ import unicode_literals
 
+import datetime
+import six
+
+from django.http import HttpResponse
+
+
+def csv_format(data):
+    """
+    Encapsulate any data which contains a comma within double quotes.
+    """
+    csv = []
+    for value in data:
+
+        # Represent None or False with empty string
+        if value in [None, False]:
+            csv.append('')
+            continue
+
+        # Convert dates to ISO format
+        if isinstance(value, (datetime.date, datetime.datetime)):
+            value = value.isoformat()
+
+        # Force conversion to string first so we can check for any commas
+        if not isinstance(value, six.string_types):
+            value = '{}'.format(value)
+
+        # Double-quote the value if it contains a comma
+        if ',' in value:
+            csv.append('"{}"'.format(value))
+        else:
+            csv.append('{}'.format(value))
+
+    return ','.join(csv)
+
+
+def queryset_to_csv(queryset):
+    """
+    Export a queryset of objects as CSV, using the model's to_csv() method.
+    """
+    output = []
+
+    # Start with the column headers
+    headers = ','.join(queryset.model.csv_headers)
+    output.append(headers)
+
+    # Iterate through the queryset
+    for obj in queryset:
+        data = csv_format(obj.to_csv())
+        output.append(data)
+
+    # Build the HTTP response
+    response = HttpResponse(
+        '\n'.join(output),
+        content_type='text/csv'
+    )
+    filename = 'netbox_{}.csv'.format(queryset.model._meta.verbose_name_plural)
+    response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
+
+    return response
+
 
 def foreground_color(bg_color):
     """

+ 1 - 1
netbox/utilities/views.py

@@ -20,7 +20,7 @@ from django.views.generic import View
 from django_tables2 import RequestConfig
 
 from extras.models import CustomField, CustomFieldValue, ExportTemplate, UserAction
-from utilities.csv import queryset_to_csv
+from utilities.utils import queryset_to_csv
 from utilities.forms import BootstrapMixin, CSVDataField
 from .error_handlers import handle_protectederror
 from .forms import ConfirmationForm