Предварительная выборка огромных наборов вопросов в django
TLDR: как предотвратить появление очень больших наборов IN в генерируемом SQL-запросе?
Когда я предварительно извлекаю поле ManyToMany, указывающее на модель Tag, из модели Object:
obj_qs = models.Object.objects.filter(created_time__gt = 2024)
obj_qs.prefetch_related('tags')
Я получаю предварительную выборку SQL-запроса следующего вида:
SELECT (`main_object_tag`.`object_id`) AS `_prefetch_related_val_object_id`, `main_tag`.`id`, `main_tag`.`name` FROM `main_tag` INNER JOIN `main_object_tag` ON (`main_tag`.`id` = `main_object_tag`.`tag_id`) WHERE `main_object_tag`.`object_id` IN (1, 2, 3, 4, 5); args=(1, 2, 3, 4, 5)
Проблема в том, что при таком подходе предложение IN может быть огромным - в моем случае сотни тысяч объектов. Можно ли это как-то обойти - использовать запрос вместо спецификации «IN»?
Экстра: почему?
Впоследствии мы будем сериализовать объекты следующим образом:
return JsonResponse([
{
'name': obj.name,
'tags': [tag.name for tag in obj.tags.all()]
} for obj in obj_qs
])
Если сумма слишком велика, можно работать с chunks, и таким образом получать набор запросов кусками по ~10'000 элементов, например.
Вы можете использовать .iterator(…)
[Django-doc] для этого:
return JsonResponse(
[
{'name': obj.name, 'tags': [tag.name for tag in obj.tags.all()]}
for obj in obj_qs.iterator(10000)
]
)
Другая идея - ограничить объем передаваемых данных, извлекая только соответствующие данные из объекта и тега, в данном случае .name
, используя .only(…)
[Django-doc]:
from django.db.models import Prefetch
obj_qs = models.Object.objects.filter(created_time__gt=2024).only('name')
obj_qs.prefetch_related(Prefetch('tags', Tag.objects.only('pk', 'name')))