【问题标题】:Export set of data in different formats以不同格式导出数据集
【发布时间】:2009-06-30 23:02:17
【问题描述】:

我希望能够根据 url 参数以不同的方式显示一组数据。

我的网址看起来像 /page/{limit}/{offset}/{format}/。

例如:

/page/20/0/xml/ - subset [0:20) in xml
/page/100/20/json/ - subset [20:100) in json

我也希望能够对 csv、text、excel、pdf、html 等做同样的事情......

我必须能够为不同的格式设置不同的 mimetypes 和 content-types。 对于 XML 应该是 application/xhtml+xml,对于 csv - text/plain 等...

在 HTML 模式下,我希望能够将此数据传递到某个模板中(我正在使用 Django)。

我打算让设置看起来像:

dataset = {
    "meta" : {"offset" : 15, "limit" : 10, "total" : 1000},
    "columns" : {"name" : "Name", "status" : "Status", "creation_date" : "Creation Date"}
    "items" : 
        [
            {"name" : "John Smith", "status" : 1, "creation_date" : "2009-06-30 10:10:09"},
            {"name" : "Joe The Plummer", "status" : 2, "creation_date" : "2009-06-30 10:10:09"}
        ]
};

并有这样的输出:

CSV 输出:

Name, Status, Creation Date
John Smith, 1, 2009-06-30 10:10:09
Joe The Plummer, 2, 2009-06-30 10:10:09

XML 输出:

<items>
    <item id="1">
        <name>John Smith</name>
        <status>1</status>
        <creation_date>2009-06-30 10:10:09</creation_date>
    </item>
    <item id="2">
        <name>Joe The Plummer</name>
        <status>2</status>
        <creation_date>2009-06-30 10:10:09</creation_date>
    </item>
</items>

所以我认为我已经为每种类型实现了自己的渲染器——比如 XMLRenderer、RSSRenderer、JSONRenderer 等......

if format == "xml":
    context = XMLRenderer().render(data = dataset)

    return HttpResponse(content, mimetype="application/xhtml+xml")
elif format == "json":
    context = JSONRenderer().render(data = dataset)

    return HttpResponse(content, mimetype="text/plain")
elif format == "rss":
    context = RSSRenderer(title="Some long title here", link="/page/10/10/rss/").render(data = dataset)

    return HttpResponse(content, mimetype="application/xhtml+xml")

# few more formats...

else:
    return render_to_response(SOME_TEMPLATE, dataset)

这是正确的方法吗?

【问题讨论】:

    标签: python django rendering


    【解决方案1】:

    我建议让渲染器也知道 mimetype,而不是在调用渲染器的代码中硬编码后者——最好将特定于格式的知识集中在一个地方,这样调用代码就会是

    content, mimetype = renderer().render(data=dataset)
    return HttpResponse(content, mimetype=mimetype)
    

    另外,这对于注册表设计模式来说是一个很好的机会(if/elif 的大多数长树都是,但是你基本上决定使用哪个对象或类是完美的!-)。所以你要么硬编码一个字典:

    format2renderer = dict(
      xml=XMLRenderer,
      rss=RSSRenderer,
      # ...etc...
    )
    

    或者甚至更好地让渲染器在启动时将自己注册到字典中,但这可能太高级/难以安排。无论哪种情况,在我刚刚引用的调用 sn-p 之前您所拥有的只是:

    renderer = format2renderer.get(format)
    if renderer is not None: ...
    

    None 时,您可以应用您的默认代码。我发现 dict 查找和多态性比 if/elif 树更容易维护和增强!-)

    【讨论】:

    • 在 Renderer 类中有默认的 mimetype 是个好主意,谢谢。但我不确定是否在 render() 调用时返回它。我将在我的父 Renderer 类中有方法 get_mimetype() 。例如 json_renderer = JSONRenderer();内容 = json_renderer.render(数据 = 数据集);返回 HttpResponse(内容,mimetype=json_renderer.get_mimetype())。我更喜欢这种方式,因为将来我可能会将数据渲染到文件或日志或其他任何东西,所以我不必知道 mimetype 作为 render() 的第二个返回参数。谢谢。
    • 我在考虑 dict 和 Registry 设计模式,结果发现它不适合我。因为对于某些 Renderer-s,我必须在构造函数中传递额外的参数,例如 RSSRenderer(title = "Some title", id = "example.com/bla/") 的 "title" 和 "id",或者为 XMLRenderer(xslt = " 传递给 xslt example.com/bla.xslt")。谢谢。
    • dict/registry 即使在某些渲染器需要参数时仍然可以很好地工作:只需注册三元组 (renderer, a, kw) 其中 a 和 kw 是位置参数的元组和命名参数的字典(如果没有,则每个为空),尽快将 None 的情况排除在外,然后调用 renderer(format, *a, **kw)。等效地,您当然可以使用 functools.partial。
    【解决方案2】:

    是的,这是正确的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-18
      • 2021-05-23
      • 1970-01-01
      • 1970-01-01
      • 2022-11-28
      相关资源
      最近更新 更多