H22
H22

Reputation: 69

I want know the rank in a Django queryset

I have a product list model and would like to know the ranking of a specific price of this model.

sorted_product_list = Product.objects.all().order_by('-price')
my_product = {'id': 10, 'price': 20000}

django has RowNum class but it is not support for mysql i have only one idea that use enumerate

for rank, element in enumerate(sorted_product_list):
    if element.id == my_product.id:
        my_product_rank = rank

Is there any other solution?

Upvotes: 4

Views: 3130

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476584

We can obtain the rank by Counting the number of Products with a higher price (so the ones that would have come first), so:

rank = Product.objects.filter(price__gt=myproduct['price']).count()

Or in case we do not know the price in advance, we can first fetch the price:

actual_price = Product.objects.values_list('price', flat=True).get(id=myproduct['id'])
rank = Product.objects.filter(price__gt=actual_price).count()

So instead of "generating" a table, we can filter the number of rows above that row, and count it.

Note that in case multiple Products have the same price, we will take as rank the smallest rank among those Products. So if there are four products with prices $ 200, $ 100, $100, and $ 50, then both Products with price $ 100 will have rank 1. The Product that costs $ 50 will have rank 3. In some sense that is logical, since there is no "internal rank" among those products: the database has the freedom to return these products in any way it wants.

Given there is an index on the price column (and it is a binary tree), this should work quite fast. The query will thus not fetch elements from the database.

In case the internal rank is important, we can use an approach where we first determine the "external rank", and then iterate through Products with the same price to determine the "internal rank", but note that this does not make much sense, since between two queries, it is possible that this "internal order" will change:

# rank that also takes into account *equal* prices, but *unstable*
actual_price = Product.objects.values_list('price', flat=True).get(id=myproduct['id'])
rank = Product.objects.filter(price__gt=actual_price).count()
for p in Product.objects.filter(price=actual_price):
    if p.id != myproduct['id']:
        rank += 1
    else:
        break

we thus keep incrementing while we have not found the product, in case we have, we stop iterating, and have obtained the rank.

Upvotes: 5

Related Questions