I got some Django model objects with different rating field values:
puzzles_rating = [0, 123, 245, 398, 412, 445, 556, 654, 875, 1000]
for rating in puzzles_rating:
puzzle = Puzzle(rating=rating)
puzzle.save()
Now, for a user_rating = 500
, I want to select the puzzle with the closest rating match. In the case above it should be puzzle #6 with rating 445.
The issue is that I can't just do:
puzzle = Puzzle.objects.filter(rating__lte=user_rating).order_by('-rating')[0]
as, in general, my closest match rating might be greater than target rating.
Is there a handy way to query the closest match from both directions?
You can use the extra
method:
puzzle = Puzzle.objects.extra(select={
'abs_diff': 'ABS(`rating` - %s)',
}, select_params=(rating,)).order_by('abs_diff').first()
From Django 1.8 onwards you don't need to write raw SQL, you can use Func
:
from django.db.models import Func, F
puzzle = Puzzle.objects.annotate(abs_diff=Func(F('rating') - rating, function='ABS')).order_by('abs_diff').first()
You can get both Puzzle objects and compare in Python which is closer:
# Note, be sure to check that puzzle_lower and puzzle_higher are not None
puzzle_lower = Puzzle.objects.filter(rating__lte=user_rating).order_by('-rating').first()
puzzle_higher = Puzzle.objects.filter(rating__gte=user_rating).order_by('rating').first()
# Note that in a tie, this chooses the lower rated puzzle
if (puzzle_higher.rating - user_rating) < abs(puzzle_lower.rating - user_rating):
puzzle = puzzle_higher
else:
puzzle = puzzle_lower
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With