diff --git a/backend/leaderboard/serializers.py b/backend/leaderboard/serializers.py index ea387ffb..3c17eff5 100644 --- a/backend/leaderboard/serializers.py +++ b/backend/leaderboard/serializers.py @@ -49,10 +49,12 @@ def get_referral_points(self, obj): def get_active_validators_count(self, obj): """ - Get count of active validator wallets for this user's validator. - Returns null if user has no validator wallets linked, otherwise returns the active count. - Uses annotated values from queryset to avoid N+1 queries. + Get count of active validator wallets for validator-related leaderboards. + Returns null for other leaderboard types. """ + if obj.type not in {'validator', 'validator-waitlist', 'validator-waitlist-graduation'}: + return None + # Use annotated values if available (from optimized queryset) if hasattr(obj, '_total_validators_count') and hasattr(obj, '_active_validators_count'): # If no validator wallets at all, return null diff --git a/backend/leaderboard/views.py b/backend/leaderboard/views.py index 81bd114a..692381a6 100644 --- a/backend/leaderboard/views.py +++ b/backend/leaderboard/views.py @@ -72,35 +72,42 @@ def get_queryset(self): 'user__creator' ) - # Annotate with validator wallet counts to avoid N+1 queries - queryset = queryset.annotate( - _active_validators_count=Count( - 'user__validator__validator_wallets', - filter=Q(user__validator__validator_wallets__status='active') - ), - _total_validators_count=Count( - 'user__validator__validator_wallets' - ) - ) - # Filter by user address if provided user_address = self.request.query_params.get('user_address') + leaderboard_type = self.request.query_params.get('type') + if user_address: queryset = queryset.filter(user__address__iexact=user_address) # When filtering by user, don't apply type filter unless explicitly provided - leaderboard_type = self.request.query_params.get('type') if leaderboard_type: queryset = queryset.filter(type=leaderboard_type) else: # Get type from query params - leaderboard_type = self.request.query_params.get('type') - if leaderboard_type: queryset = queryset.filter(type=leaderboard_type) else: # Default to validator leaderboard only when not filtering by user + leaderboard_type = 'validator' queryset = queryset.filter(type='validator') + # Validator wallet count annotations are expensive and only needed + # for validator-oriented leaderboards. + needs_validator_counts = leaderboard_type in { + 'validator', + 'validator-waitlist', + 'validator-waitlist-graduation', + } + if needs_validator_counts: + queryset = queryset.annotate( + _active_validators_count=Count( + 'user__validator__validator_wallets', + filter=Q(user__validator__validator_wallets__status='active') + ), + _total_validators_count=Count( + 'user__validator__validator_wallets' + ) + ) + # Handle rank ordering order = self.request.query_params.get('order', 'asc') if order == 'desc': @@ -109,7 +116,6 @@ def get_queryset(self): queryset = queryset.order_by('rank') return queryset - def list(self, request, *args, **kwargs): """ Override to apply limit after filtering/ordering. @@ -521,3 +527,4 @@ def trending(self, request): return Response(results) +