【问题标题】:need a recursive CSV serializer for Django tastypie需要一个用于 Django 美味派的递归 CSV 序列化程序
【发布时间】:2014-05-27 21:41:39
【问题描述】:

我已经为我的 Django 应用程序创建了一个简单的 GET only api,使用的是 sweetpie。我需要提供平面表格 CSV 数据,但我的数据库结构已标准化。 Per the documentation 我已经实现了一个自定义的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 时),所以我最终得到包含嵌套 JSON 数据的 CSV 数据,看起来像这样。

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 之前合并结果,但到目前为止还没有成功。

【问题讨论】:

  • 您是否正在寻找一些代码来转换您的第三个元素,例如:line[:2] + eval(line[2]).values() 在 lambda 函数中?
  • 这听起来很有希望,但我对 lambda 函数一无所知。它需要是通用的,因为我不想假设它总是需要转换的行中的第三个元素,因为不同的模型有不同的外键。所以需要先检测它是 JSON,然后应用转换。
  • 我认为使用DictWriter 会更好。我设想乱序数据。
  • @andrew-barr 关于您的需求,我认为 lambda 函数不匹配。我在下面的答案中向您提出了一个更通用的函数,它既不依赖于 JSON 字符串的位置,也不依赖于元素的数量。

标签: python django csv serialization tastypie


【解决方案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)

【讨论】:

  • 我喜欢这个外观!非常感谢。明天我试一试时,我一定会告诉你它是如何工作的/接受答案。
  • 这行得通,在我调整之后。我提交了您的答案的编辑,它在展平后的第一次迭代中获取标题值(字典键)。
【解决方案2】:

您可以在您拥有的每一行上使用以下函数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 字符串的位置。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-02
    • 1970-01-01
    • 2013-03-25
    • 1970-01-01
    • 2013-03-28
    • 2022-01-22
    • 1970-01-01
    相关资源
    最近更新 更多