使用 GraphQL 机制,但返回 CSV

Mig*_*ell 7 csv mime-types graphql graphene-python

普通的 REST API 可能会让您以不同的格式请求相同的数据,并使用不同的Accept标头,例如application/json、 或text/html,或text/csv格式化响应。

但是,如果您使用 GraphQL,则 JSON 似乎是唯一可接受的返回内容类型。但是,我需要我的 API 能够返回 CSV 数据,以供无法理解 JSON 的不太复杂的客户端使用。

如果给定标头,GraphQL 端点返回 CSV 数据是否有意义Accept: text/csv?如果没有,是否有更好的练习方法来做到这一点?

这更多的是一个概念性问题,但我专门使用石墨烯来实现我的 API。它是否提供任何处理自定义内容类型的机制?

小智 5

是的,你可以,但它不是内置的,你必须覆盖一些东西。这更像是一种解决方法。

执行以下步骤,您将获得 csv 输出:

  1. 添加csv = graphene.String()您的查询并将其解决为您想要的任何内容。

  2. 创建一个新类继承GraphQLView

  3. 重写dispatch函数看起来像这样:

    def dispatch(self, request, *args, **kwargs):
        response = super(CustomGraphqlView, self).dispatch(request, *args, **kwargs)
        try:
            data = json.loads(response.content.decode('utf-8'))
            if 'csv' in data['data']:
                data['data'].pop('csv')
                if len(list(data['data'].keys())) == 1:
                    model = list(data['data'].keys())[0]
                else:
                    raise GraphQLError("can not export to csv")
                data = pd.json_normalize(data['data'][model])
                response = HttpResponse(content_type='text/csv')
                response['Content-Disposition'] = 'attachment; filename="output.csv"'
    
                writer = csv.writer(response)
                writer.writerow(data.columns)
                for value in data.values:
                    writer.writerow(value)
        except GraphQLError as e:
            raise e
        except Exception:
            pass
        return response
    
    Run Code Online (Sandbox Code Playgroud)
  4. 导入所有必要的模块

  5. GraphQLView将文件中的默认值替换urls.py为新的视图类。

现在,如果您在 GraphQL 查询中包含“csv”,它将返回原始 csv 数据,然后您可以将数据保存到前端的 csv 文件中。示例查询如下:

query{
  items{
    id
    name
    price
    category{
        name
    }
  }
  csv
}
Run Code Online (Sandbox Code Playgroud)

请记住,这是一种获取 csv 格式的原始数据的方法,您必须保存它。您可以使用以下代码在 JavaScript 中执行此操作:

req.then(data => {
    let element = document.createElement('a');
    element.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(data.data));
    element.setAttribute('download', 'output.csv');

    element.style.display = 'none';
    document.body.appendChild(element);

    element.click();

    document.body.removeChild(element);
})
Run Code Online (Sandbox Code Playgroud)

此方法会展平 JSON 数据,因此不会丢失任何数据。


Her*_*rku 0

GraphQL 依赖于(并因此而发光)响应嵌套数据。据我了解,CSV 只能显示平面键值对。这使得 CSV 不太适合 GraphQL 响应。

我认为实现您想要做的事情的最干净的方法是将 GraphQL 客户端放在您的客户面前:

+------+  csv  +-------+  http/json  +------+
|client|<----->|adapter|<----------->|server|
+------+       +-------+             +------+
Run Code Online (Sandbox Code Playgroud)

这里的好处是您的适配器只需能够将其指定的查询转换为 CSV。

显然你可能并不总是能够这样做(但是你如何让他们发送 GraphQL 查询)。或者,您可以构建一个将 JSON 转换为 CSV 的中间件。但接下来你必须处理整个 GraphQL 规范。祝你翻译此回复好运:

{
  "__typename": "Query",
  "someUnion": [
    { "__typename": "UnionA", "numberField": 1, "nested": [1, 2, 3, 4] },
    { "__typename": "UnionB", "stringField": "str" },
  ],
  "otherField": 123.34
}
Run Code Online (Sandbox Code Playgroud)

因此,如果您无法通过 HTTP 传输 CSV,那么 GraphQL 就是错误的选择,因为它不是为此而构建的。如果您不允许那些难以转换为 CSV 的 GraphQL 功能,那么您就不再拥有 GraphQL,因此将其称为 GraphQL 就没有意义了。