Как эффективно использовать запрос 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.

Другие вопросы по тегам