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

问题描述 投票:1回答:1

我正在使用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)

以上打印ids 1到40000按预期方式。

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

以上仅打印1到100的ID

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

以上仅打印1到200的ID

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

在使用queryset迭代器时,是否根本无法进行另一个查询?还有另一种方法可以达到同样的目的吗?

谢谢

django django-1.11 django-mysql
1个回答
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与其他查询的使用。

© www.soinside.com 2019 - 2024. All rights reserved.