Нужно ли обращаться к кэшу Django _prefetched_objects_cache, чтобы решить N+1 запрос?
У меня есть следующий код шаблона Django с N+1 запросом:
{% for theobject in objs %}
{% for part in theobject.parts_ordered %}
<li>{{ part }}</li>
{% endfor %}
{% endfor %}
Вот parts_ordered на TheObject:
class TheObject:
# ...
def parts_ordered(self) -> list["Part"]:
return self.parts.all().order_by("pk")
А вот объект Part:
class Part:
# ...
theobject = models.ForeignKey(
TheObject, on_delete=models.CASCADE, related_name="parts"
)
и вот префетч, получающий objs:
ofs = ObjectFormSet(
queryset=TheObject.objects
.filter(objectset=os)
.prefetch_related("parts")
)
Я думаю, что order_by("pk")
нарушает предварительную выборку.
Вот что рекомендует chatgpt, и это работает (больше нет N+1 запросов, результаты кажутся одинаковыми):
class TheObject:
# ...
def parts_ordered(self) -> list["Part"]:
if (
hasattr(self, "_prefetched_objects_cache")
and "parts" in self._prefetched_objects_cache
):
# Use prefetched data and sort in Python
return sorted(
self._prefetched_objects_cache["parts"], key=lambda cc: cc.pk
)
# Fallback to querying the DB if prefetching wasn’t used
return self.parts.all().order_by("pk")
Стоит ли мне полагаться на _prefetched_objects_cache? Есть ли лучший способ?
В вашем случае я бы не стал использовать закрытые методы или атрибуты. Как правило, авторы библиотек помечают их как закрытые по какой-либо причине. Чаще всего это детали реализации, которые могут измениться в следующей версии, что может привести к тому, что ваш код перестанет работать.
В вашем случае вы можете упростить задачу, но все равно решить проблему N + 1
. Для этого вы можете использовать Prefetch
object и этот запрос:
from django.db import models
ofs = ObjectFormSet(
queryset=TheObject.objects
.filter(objectset=os)
.prefetch_related(
models.Prefetch(
lookup='parts',
queryset=Part.objects.order_by('pk'),
to_attr='parts_ordered',
),
)
)
Это даст результаты, аналогичные вашим, и должно немного повысить общую производительность, поскольку база данных выполняет сортировку, а не python
.