Skip to content

Commit 0c553cf

Browse files
committed
Merge pull request #3 from robguttman/master
Addresses issue #2 - maintains original header/field order.
2 parents 42d4757 + da427d5 commit 0c553cf

File tree

5 files changed

+31
-11
lines changed

5 files changed

+31
-11
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@ dist/
44
build/
55
*.egg-info/
66
*~
7+
8+
/.settings/
9+
/.pydevproject
10+
/.project

rest_framework_csv/orderedrows.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class OrderedRows(list):
2+
"""
3+
Maintains original header/field ordering.
4+
"""
5+
def __init__(self, header):
6+
self.header = header

rest_framework_csv/parsers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from rest_framework.parsers import BaseParser
44
from rest_framework.exceptions import ParseError
5+
from rest_framework_csv.orderedrows import OrderedRows
56

67

78
class CSVParser(BaseParser):
@@ -19,10 +20,9 @@ def parse(self, stream, media_type=None, parser_context=None):
1920

2021
try:
2122
rows = csv.reader(stream, delimiter=delimiter)
22-
header = rows.next()
23-
data = []
23+
data = OrderedRows(rows.next())
2424
for row in rows:
25-
row_data = dict(zip(header, row))
25+
row_data = dict(zip(data.header, row))
2626
data.append(row_data)
2727
return data
2828
except Exception, exc:

rest_framework_csv/renderers.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from collections import defaultdict
33
from rest_framework.renderers import *
44
from StringIO import StringIO
5+
from rest_framework_csv.orderedrows import OrderedRows
56

67
class CSVRenderer(BaseRenderer):
78
"""
@@ -43,23 +44,24 @@ def tablize(self, data):
4344
# fall into.
4445
data = self.flatten_data(data)
4546

46-
# Get the set of all unique headers, and sort them.
47-
headers = set()
48-
for item in data:
49-
headers.update(item.keys())
50-
headers = sorted(headers)
47+
# Get the set of all unique headers, and sort them (unless already provided).
48+
if not data.header:
49+
headers = set()
50+
for item in data:
51+
headers.update(item.keys())
52+
data.header = sorted(headers)
5153

5254
# Create a row for each dictionary, filling in columns for which the
5355
# item has no data with None values.
5456
rows = []
5557
for item in data:
5658
row = []
57-
for key in headers:
59+
for key in data.header:
5860
row.append(item.get(key, None))
5961
rows.append(row)
6062

6163
# Return your "table", with the headers as the first row.
62-
return [headers] + rows
64+
return [data.header] + rows
6365

6466
else:
6567
return []
@@ -70,7 +72,7 @@ def flatten_data(self, data):
7072
each exactly one level deep. The key for each value in the dictionaries
7173
designates the name of the column that the value will fall into.
7274
"""
73-
flat_data = []
75+
flat_data = OrderedRows(data.header if hasattr(data, 'header') else None)
7476
for item in data:
7577
flat_item = self.flatten_item(item)
7678
flat_data.append(flat_item)

rest_framework_csv/tests.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ def test_render_a_list_with_unicode_elements(self):
6969
dump = renderer.render([{u'a': 1, u'b': u'hello\u2014goodbye', u'c': 'http://example.com/'}])
7070
self.assertEqual(dump, (u'a,b,c\r\n1,hello—goodbye,http://example.com/\r\n').encode('utf-8'))
7171

72+
def test_render_ordered_rows(self):
73+
parser = CSVParser()
74+
csv_file = 'v1,v2,v3\r\na,1,2.3\r\nb,4,5.6\r\n'
75+
data = parser.parse(StringIO(csv_file))
76+
renderer = CSVRenderer()
77+
dump = renderer.render(data)
78+
self.assertEqual(dump, csv_file) # field order should be maintained
79+
7280

7381
class TestCSVParser(TestCase):
7482

0 commit comments

Comments
 (0)