Django - 使用查询集迭代器时无法执行另一个查询()

Django - Cannot perform another query while using a queryset iterator()

我在 MySQL 中使用 Django 1.11。短期内升级到 2 是不可行的,因此不是我当前问题的 acceptable 解决方案,但参考 Django 2 的答案可能会帮助其他人,所以请随时 post 他们。

我需要对 table 中的所有行执行数据迁移。少于 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)

上面按预期打印了 ids 1 到 40000。

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

上面只打印ids 1到100

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

上面只打印ids 1到200

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

在使用查询集迭代器时是否根本不可能进行另一个查询?有没有其他方法可以达到同样的目的?

谢谢

正如@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 与其他查询的使用。