需要一个Django Tastypie的递归CSV序列化器
我为我的Django应用程序创建了一个简单的只支持GET请求的API,使用了tastypie这个工具。我需要提供扁平化的表格形式的CSV数据,但我的数据库结构是经过规范化的。根据文档,我实现了一个自定义的Serializer
类,并添加了一个to_csv()
方法,如下所示。
def to_csv(self, data, options=None):
options = options or {}
data = self.to_simple(data, options)
raw_data = StringIO.StringIO()
writer = csv.writer(raw_data, quotechar="'", quoting=csv.QUOTE_NONNUMERIC)
if "meta" in data.keys():#if multiple objects are returned
objects = data.get("objects")
writer.writerow(objects[0].keys())
for object in objects:
test = object.values()
writer.writerow(test)
else:
writer.writerow(data.values())
CSVContent=raw_data.getvalue()
return CSVContent
这个方法运行得很好,但默认情况下,任何资源都会以JSON格式呈现(当我在ModelResource的ForeignKey定义中包含full = True
时),所以我得到的CSV数据中包含了嵌套的JSON数据,像这样。
foodID,foodName,related_details
1,"apricot","{'type':'fruit', 'cost':'medium'}"
2,"beef","{'type':'animal', 'cost':'high'}"
3,"celery","{'type':'vegetable', 'cost':'low'}"
我想要的输出是
foodID,foodName,type,cost
1,"apricot","fruit","medium"
2,"beef","animal","high"
3,"celery","vegetable","low"
我有一个想法,可能需要递归地应用我的序列化器,然后在写入CSV之前将结果合并,但到目前为止还没有成功。
2 个回答
0
你可以在每一行上使用下面这个函数 to_list
:
def to_list(line):
idx = -1
for i, l in enumerate(line):
if type(l) is str and '{' in l and '}' in l:
idx = i
break
if idx != -1:
result = line[:idx] + eval(line[idx]).values() + line[idx+1:]
else:
result = line
return result
if __name__ == "__main__":
lst = [[1,"apricot","{'type':'fruit', 'cost':'medium'}"],
["beef","{'type':'animal', 'cost':'high'}", 3],
["meat", "sugar"],
["{'type':'car', 'cost':'nothing'}", "something"]]
for line in lst:
print to_list(line)
那么对于以下这些列表:
1, "apricot", "{'type':'fruit', 'cost':'medium'}"
"beef", "{'type':'animal', 'cost':'high'}", 3
"meat", "sugar"
"{'type':'car', 'cost':'nothing'}", "something"
你会得到:
1, 'apricot', 'medium', 'fruit'
'beef', 'high', 'animal', 3
'meat', 'sugar'
'nothing', 'car', 'something'
正如你所看到的,这个方法不管元素的数量,也不管JSON字符串的位置。
1
def to_csv(self, data, options=None):
options = options or {}
data = self.to_simple(data, options)
raw_data = StringIO.StringIO()
first = True
if "meta" in data.keys():#if multiple objects are returned
objects = data.get("objects")
for value in objects:
test = {}
self.flatten(value, test)
if first:
writer = csv.DictWriter(raw_data, test.keys(), quotechar="'", quoting=csv.QUOTE_NONNUMERIC)
writer.writeheader()
writer.writerow(test)
first=False
else:
writer.writerow(test)
else:
test = {}
self.flatten(data, test)
if first:
writer = csv.DictWriter(raw_data, test.keys(), quotechar="'", quoting=csv.QUOTE_NONNUMERIC)
writer.writeheader()
writer.writerow(test)
first=False
else:
writer.writerow(test)
CSVContent=raw_data.getvalue()
return CSVContent
def flatten(self, data, odict = {}):
if isinstance(data, list):
for value in data:
self.flatten(value, odict)
elif isinstance(data, dict):
for (key, value) in data.items():
if not isinstance(value, (dict, list)):
odict[key] = value
else:
self.flatten(value, odict)
当然可以!请把你想要翻译的内容发给我,我会帮你把它变得更简单易懂。