Как эффективно использовать запрос Django и Q для фильтрации каждого объекта в наборе запросов и возврата 1 значения поля для каждого уникального поля в наборе запросов
У меня есть запрос, который возвращает следующий набор запросов:
results = <QuerySet [<Product: ItemA>, <Product: ItemA>, <Product: ItemB>, <Product: ItemB>, <Product: ItemB>, <Product: ItemC>, <Product: ItemC>]>
__str__
представление модели name
и каждый Product
изменение, вероятно, имеет другое значение для price
поле. После этого запроса мне нужно искать в моей базе данных для каждого Product
в запросе и вернуть самую низкую цену для каждого уникального name
так как:
Lowest price for all in database where name is == to ItemA
Lowest price for all in database where name is == to ItemB
Lowest price for all in database where name is == to ItemC
Я использую следующий блок кода для достижения этой цели:
query_list = []
for each in results:
if each.name not in query_list: #Checks if the name of the object is not in in the query list
query_list.append(each.name) #Adds just the name of the objects so there is just one of each name in query_list
for each in query_list:
priced = results.filter(name=each).order_by('price').first() #Lowest price for each name in query_list
Это очень неэффективно. Есть ли способ сделать подобное вычисление, не добавляя уникальное имя каждого Product
в отдельный список, итерации по этому списку, а затем сделать запрос для каждого? Я чувствую, что есть способ использовать тип сложного поиска для достижения моих целей, может быть, событие использует меньше Python и заставить БД выполнять больше работы, но вышеизложенное - лучшее, что я смог выяснить, так далеко. Там может быть много разных хитов в results
поэтому мне нужно, чтобы этот блок был максимально эффективным
1 ответ
Это легко после прочтения документации. Создание агрегатов для каждого элемента в QuerySet, а также "Взаимодействие с упорядочением по умолчанию или order_by()".
from django.db.models import Min
prices = {x['name']: x['lowest_price']
for x in results.values('name').annotate(lowest_price=Min('price').order_by()}
for product in results:
if product.name in prices and product.price == prices[product.name]:
priced = row # output the row
del prices[product.name]
Это выполняется двумя запросами к базе данных.
Возможно даже более эффективное решение с одним запросом возможно с помощью функции Window, но для этого требуется расширенная база данных базы данных, и оно не может работать, например, в тестах с sqlite3.