Django-使用queryset iterator()时无法执行其他查询

时间:2019-04-16 13:15:01

标签: django django-1.11 django-mysql

我正在将Django 1.11与MySQL配合使用。短期内升级到2是不可行的,因此对于我眼前的问题也不是可接受的解决方案,但是有关Django 2的答案可能会帮助其他人,所以可以随时发布它们。

我需要对表中的所有行执行数据迁移。少于40000行,但它们很大-两列是〜15KB的JSON,在加载模型时会对其进行解析。 (这些是我在数据迁移中需要使用的行,因此我无法defer它们行)

为了避免将所有对象同时加载到内存中,我认为我会使用queryset.iterator,它一次只能解析100行。如果我所做的只是读取结果,则此方法很好,但是如果我执行另一个查询(例如,对save个对象进行查询),则一旦达到当前100个结果块的末尾,下一个100个块结果无法获取,并且迭代器完成。

好像fetchmany从中获取行的结果集丢失了。

为了说明使用./manage.py shell的情况 (假设存在40000个具有顺序ID的MyModel)

iterator = app.models.MyModel.objects.iterator()
for obj in iterator:
  print(obj.id)

上面的代码会按预期打印ID 1到40000。

iterator = app.models.MyModel.objects.iterator()
for obj in iterator:
  print(obj.id)
  obj.save()

以上仅显示ID 1到100

iterator = app.models.MyModel.objects.iterator()
for obj in iterator:
  print(obj.id)
  if obj.id == 101:
    obj.save()

以上仅显示ID 1至200

obj.save替换为对数据库进行查询的其他任何内容(例如app.models.OtherModel.objects.first())。

使用queryset迭代器时是否根本无法进行其他查询?还有另一种方法可以实现相同的目的吗?

谢谢

1 个答案:

答案 0 :(得分:0)

如@dirkgroten所建议,Paginator是迭代器的替代方案,它在内存使用方面可能是更好的解决方案,因为它对查询集使用切片,并添加了OFFSET和LIMIT子句以仅检索部分完整结果集。

但是,较高的OFFSET值会导致MySQL性能下降:https://www.eversql.com/faster-pagination-in-mysql-why-order-by-with-limit-and-offset-is-slow/

因此,在索引列上进行搜索可能是一个更好的选择:

chunk_size = 100
seek_id = 0
next_seek_id = -1
while seek_id != next_seek_id:
  seek_id = next_seek_id
  for obj in app.models.MyModel.objects.filter(id__gt=seek_id)[:chunk_size]:
    next_seek_id = obj.id
    # do your thing

此外,如果您的数据使得执行查询并不昂贵,但是实例化模型实例非常昂贵,则迭代器具有执行单个数据库查询的潜在优势。希望其他答案能够阐明将queryset.iterator与其他查询一起使用。