Reputation: 69
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
Reputation: 476584
We can obtain the rank by Count
ing the number of Product
s 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 Product
s have the same price, we will take as rank the smallest rank among those Product
s. So if there are four products with prices $ 200, $ 100, $100, and $ 50, then both Product
s 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 Product
s 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