【问题标题】:Format Django QuerySet to output values as an array格式化 Django QuerySet 以将值输出为数组
【发布时间】:2018-03-26 03:03:45
【问题描述】:

我的表格中的数据如下所示:

src_id, dst_id, params
int   , int   , array

我正在查询数据以从数组中提取一些值 使用以下 Django 查询

dataset = query_set.values_list('src_id', 'dst_id', *[e.field for e in settings])

我需要这样输出数据:

[
[1,2,[a,b,c,d]],
[3,4,[a,c,d,e]],
...
]

values_list 返回一个元组列表,例如 (1,2,a,b,c,d)(3,4,a,c,d,e)...

所以我一直在执行以下操作来将元组值重新组合成一个数组:

[[d[0], d[1], d[2:]] for d in dataset]

它工作正常,但我有 1M+ 行,而且速度很慢。它也使用了大量的内存。理想情况下,我想在 SQL 中正确格式化数据(通过 Django),并将输出直接推送到 JSON 序列化程序,而不必生成中间数组。

有没有办法优化这段代码?

我研究过使用循环或 lambda,但这并没有太大区别。我看过使用array,但它只需要原始类型,所以数组数组没有太多运气。

如果可能的话,我正在寻找一种方法来查询数据库并以正确的格式直接输出数据:

我看到 Django 可以像进行切片数组查找一样

dataset = query_set.values_list('src_id', 'dst_id', 'data__3_10')

例如获取索引 3 和 10 之间的值,但我需要检索特定索引(例如 1、4、5、6、8、11),而不是切片。

关于如何格式化这样的查询输出的任何提示? 有可能,还是对 Django 来说太深奥了? 我需要raw SQL 吗?

谢谢

【问题讨论】:

  • 格式不清楚,数组中的字符串应该真的没有引号,并且没有字符串可以包含逗号或“]”。如果我知道,数组(参数)的单个元素可以用作.filter(data__3=...) 中的查找,但不能在 values_list 中引用。你是怎么做到的?
  • 这个问题首先不清楚。现在我从您的回答中了解到[1,2,[a,b,c,d]], 根本不是格式化输出,而是嵌套对象的符号表示,其中“a、b、c、d”是任何相同类型的值。一切最终都由 JSON 样式形成,这不是任何示例的一部分,因为问题是一般性的。 (现在修正了上面的错字)

标签: python django django-queryset django-postgresql


【解决方案1】:

如果数据库是 PostgreSQLOracle,那么在 Django 1.11 中,通过将iterator() 方法应用于任何查询集,可以显着降低内存需求,包括values()values_list() 查询集。然后默认使用服务器端游标,而不在 Django 或数据库驱动程序中进行任何缓存。阅读文档中的详细信息。

演示

qs = query_set.values_list('src_id', 'dst_id', 'data')
for row in qs.iterator():
    print(row) 

输出

(3, 4, ['a', 'c', 'd', 'e'])
...

完整示例

qs = query_set.values_list('src_id', 'dst_id', 'data')
with open(..., 'w') as f:
    f.write('[\n')
    for src_id, dst_id, data in qs.iterator():
        str_sub_array = ','.join(data[x] for x in column_settings)
        f.write('[{},{},[{}]],\n'.format(src_id, dst_id, str_sub_array)
    f.write(']\n')

内存要求可以忽略不计。

【讨论】:

  • 感谢您的信息。尽管我还没有使用 1.11,但这可能会有所帮助。我可以看到迭代器如何减少内存请求,但是问题更多是关于将数据格式化为查询集中的数组,同时从原始数组中只选择必要的值。我还没有找到在 Django 中做到这一点的方法,只能通过编写原始 SQL 查询。一旦数据格式正确,我只需要返回它,但没有它我需要遍历每一行,这很慢。
  • @MrE:更新了答案。如果要求很明确,格式化是微不足道的,Django 1.11 不支持命名游标的内存要求是一个难题,原始 sql 可能无济于事。我认为内存和快速循环是必要的基础。
  • 我的目标是完全消除格式化循环,让 SQL 返回正确的格式。我当前的循环最多需要 2 分钟,而查询需要 10 秒。迭代器可能会为循环节省内存,但最后我需要返回整个数据集,因此如果 SQL 一次性返回所有数据,我将节省构建输出所需的内存。似乎没有 Django 方法可以解决这个问题,所以我将使用原始 SQL。
  • 感谢 iterator() 的提示。由于 json 序列化程序接受迭代器作为输入,它最终确实有助于减少内存占用。问题的主要部分是关于在 Django 中将事物格式化为 SQL 的数组,所以我认为这不应该是公认的答案。
  • 我希望通过标题“格式化 Django QuerySet”来改进它而不是避免它。您没有粘贴任何关于格式缓慢的来源,以查看推荐的最佳实践无法轻松改进它。你没有写一个比率:number_of_used_array_field_values / length_of_array_field。一半绝对可以忽略不计,而百分之一真的很成问题。
【解决方案2】:

我能够使用extra() 功能直接在 Django 中格式化输出,而无需使用原始 SQL:

dataset = queryset.objects\
               .extra(select={'data': 'ARRAY[{}]'.format(",".join([fields])})\
               .values_list('src_id','dst_id','data')
return dataset.iterator()

这允许根据需要选择 SQL 数组中的特定索引。

返回迭代器(如@hynekcer 所建议的那样),与结果数组本身相反,实际上更适合内存。

避免了重新格式化数据的循环,迭代器可以直接发送到接受迭代器作为输入的 JSON 序列化器。

【讨论】:

  • 普通的迭代器不能被jsonsimplejson 直接序列化。通过defaultcls 参数或通过子类化JSONEncoder 可以将迭代器转换为内部列表,有一些类似的解决方案。更好的大数据解决方案是那些将类型列表与迭代器类型交叉(子类化)以结合低内存要求和直接可序列化性的解决方案。一种解决方案是我的SerializableGenerator(iterator)。如果您通过json.dump 序列化到流而不是字符串,那么输出内存要求也很低。
  • 可以编写一个不需要“.extra()”的简单“查询表达式函数”,如果查询使用更多表并且后端可以编译语法“relation_field__other_field”,则很有用没有硬编码表名。对于一个简单的案例,您的解决方案可能是最简单的。 (请原谅我的回答不完整。您的问题最初很笼统,我在第一步之后等待反馈。)
  • ujson 可以将迭代器作为输入并且工作正常。这就是我使用的。它也是目前速度更快的序列化器之一。
猜你喜欢
  • 2020-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-15
  • 1970-01-01
  • 2023-01-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多