Оптимизация запроса с помощью кэша QuerySet
Я хочу оптимизировать запросы в базу данных. У меня возникает ситуация по которой мне нужно фильтровать по одному столбцу, но по с нескольким значениям. И все отфильтрованные данные отправить в словаре.
Я знаю, что у QuerySet есть кэш. Но похоже в моей ситуации, он не работает или я делаю что-то не так.
Вот упрощенная модель и варианты работы с кэшом QuerySet.
class Meme(models.Model):
name = models.CharField(max_length=50)
meme = Meme.objects.all()
meme_list = list(meme) # тут происходит оценивание QuerySet и запись в кэш
print(meme) # берутся данные из кэша из QuerySet объекта meme
meme_name_list = []
for i in meme: # тут тоже берутся данные из кэша из QuerySet объекта meme
meme_name_list.append(i.name)
Далее код который мне нужно оптимизировать:
filtered_meme = meme.filter(name__icontains='Иван') # тут не используется кэш из QuerySet объекта meme,
# хотя в объекте meme, а значит и в кэше точно есть эти данные.
filtered_meme1 = meme.filter(name__icontains='Петя')
filtered_meme2 = meme.filter(name__icontains='Саня')
filtered_meme3 = meme.filter(name__icontains='Маша')
filtered_meme4 = meme.filter(name__icontains='Соня')
data = {
"filtered_meme": filtered_meme,
"filtered_meme1": filtered_meme1,
"filtered_meme2": filtered_meme2,
"filtered_meme3": filtered_meme3,
"filtered_meme4": filtered_meme4,
}
На каждую такую фильтрацию будет новый запрос в бд.
Можно ли такие запросы как-нибудь оптимизировать с помощью кэша QuerySet? Или какие есть альтернативы решения данной проблемы.
Один хороший человек дал решение:
import functools
from collections import defaultdict
from operator import or_
from django.db.models import Q, Case, CharField, Value, When
values = ("Иван", "Петя", "Саня")
result = defaultdict(list)
queryset = Meme.objects.annotate(
i=Case(
*[
When(
name__icontains=value,
then=Value(str(i))
)
for i, value in enumerate(values)
],
output_field=CharField()
)
).filter(
functools.reduce(
or_,
[
Q(name__icontains=value)
for value in values
]
)
)
for obj in queryset:
result["filtered_meme" + (obj.i if obj.i != "0" else "")].append(obj)
Данный подход выполняет один запрос в базу данных.