У меня есть запрос, который возвращает следующий набор запросов:
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 и заставляет db выполнять больше работы, но приведенное выше - лучшее, что я смог выяснить, поэтому далеко. В results
может быть много разных совпадений, поэтому мне нужно, чтобы этот блок был максимально эффективным.
Это легко сделать после прочтения документации Создание агрегатов для каждого элемента в 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]
Это выполняется двумя запросами к базе данных.
Еще более эффективное решение с одним запросом, вероятно, возможно с Оконная функция, но для этого требуется расширенный бэкэнд базы данных, и он не может работать, например. в тестах с sqlite3.