I'm building a leaderboard view for a REST API I'm designing in DRF. I need a bit of help in reducing an inefficiency.
views.py
class LeaderboardAPIView(ListAPIView):
serializer_class = UserSerializerLeaderboard
permission_classes = (IsAuthenticated,)
def get_queryset(self):
queryset = User.objects.all()
queryset = list(queryset)
queryset.sort(key=operator.attrgetter("total_karma"), reverse=True)
queryset = queryset[:10]
return queryset
serializers.py
class UserSerializerLeaderboard(serializers.ModelSerializer):
score = serializers.SerializerMethodField(read_only=True)
place = serializers.SerializerMethodField(read_only=True)
def get_score(self, obj):
return obj.total_karma
def get_place(self, obj):
return "1"
class Meta:
model = User
fields = ("score", "place")
models.py
@property
def total_karma(self):
return self.total_post_score() + self.total_comment_score()
def total_post_score(self):
relevant_votes = PostVote.objects.filter(post__user=self)
total = 0
for vote in relevant_votes:
total += vote.vote
return total
def total_comment_score(self):
relevant_votes = CommentVote.objects.filter(comment__user=self)
total = 0
for vote in relevant_votes:
total += vote.vote
return total
...
class PostVote(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
post = models.ForeignKey(Post, on_delete=models.SET_NULL, null=True)
vote = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ("user", "post")
def __str__(self):
return str(self.id) + " " + str(self.vote)
...
class CommentVote(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
comment = models.ForeignKey(Comment, on_delete=models.SET_NULL, null=True)
vote = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ("user", "comment")
def __str__(self):
return str(self.id) + " " + str(self.vote)
The get_place method currently returns a placeholder of 1. I'd like it to return the actual place the user is in, sorted by score. Now, I know I'm already calculating this in the queryset, so I don't want to repeat this inefficiently. How can I send the place of the user to the serializer directly, rather than repeating the calculation in the method?