Reputation:
I have the following model:
class UserDetail(models.Model):
donations = models.IntegerField(blank=True, null = True,)
points = models.IntegerField(blank=True, null = True,)
requests = models.IntegerField(blank=True, null=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
blank=True,
null=True,
)
I want to get the top 5 users with the most points and display them in an HTML leaderboard template.
Please help
EDIT:
MY VIEW:
def leaderboard(request,):
User.objects.alias(
total_points=Sum('userdetail__points')
).order_by('-total_points')[:5]
return render(request, 'leaderboard.html')
Upvotes: 1
Views: 686
Reputation: 476699
You can work with a .annotate(…)
[Django-doc] and then .order_by(…)
[Django-doc]:
from django.db.models import Sum
User.objects.annotate(
total_points=Sum('userdetail__points')
).order_by('-total_points')[:5]
Since django-3.2 you can work with .alias(…)
[Django-doc] to prevent calculating this both as column and in the ORDER BY
clause:
from django.db.models import Sum
User.objects.alias(
total_points=Sum('userdetail__points')
).order_by('-total_points')[:5]
Regardless which of the two you pick, you pass these to the context in the template:
def leaderboard(request,):
leaders = User.objects.alias(
total_points=Sum('userdetail__points')
).order_by('-total_points')[:5]
return render(request, 'leaderboard.html', {'leaders': leaders})
and in the template you can render the leaderbord with:
{% for leader in leaders %}
{{ leader.username }}
{% endfor %}}
If you want to add the points next to the username, you should work with the .annotate(…)
variant. You can then render this with:
<table>
<th><td>User</td><td>Points</td></th>
{% for leader in leaders %}
<tr><td>{{ leader.username }}</td><td>{{ leader.total_points }}</td></tr>
{% endfor %}
</table>
Upvotes: 2