Some great answers already, but none of them mentions Window functions.
The following example annotates all score objects with the latest score for the corresponding student:
from django.db.models import F, Window
from django.db.models.functions import FirstValue
scores = Score.objects.annotate(
    latest_score=Window(
        expression=FirstValue('score'),
        partition_by=['student'],
        order_by=F('date').desc(),
    )
)
This results in the following SQL (using Sqlite backend):
SELECT 
  "score"."id", 
  "score"."student_id", 
  "score"."date", 
  "score"."score", 
  FIRST_VALUE("score"."score") 
  OVER (PARTITION BY "score"."student_id" ORDER BY "score"."date" DESC) 
  AS "latest_score" 
FROM "score"
The required information is already there, but we can also reduce this queryset to a set of unique combinations of student_id and latest_score.
For example, on PostgreSQL we can use distinct with field names, as in scores.distinct('student').
On other db backends we can do something like set(scores.values_list('student_id', 'latest_score')), although this evaluates the queryset.
Unfortunately, at the time of writing, it is not yet possible to filter a windowed queryset.
EDIT: as of Django 4.2, windowed querysets can be filtered