How to sort a queryset based on a foreign key field?

Question:

This is a contest system project. I have these models and I know the contest_id and problem_id. I’m trying to return a queryset that contains users who have solved a problem. A user who solves a problem is the one whose submission’s score is equal to score of the problem he tried to solve.
At the end I need to sort these users based on the time they have submitted their successful submission.

class Contest(models.Model):
   name = models.CharField(max_length=50)
   holder = models.ForeignKey(User, on_delete=models.CASCADE)
   start_time = models.DateTimeField()
   finish_time = models.DateTimeField()
   is_monetary = models.BooleanField(default=False)
   price = models.PositiveIntegerField(default=0)
   problems = models.ManyToManyField(Problem)
   authors = models.ManyToManyField(User, related_name='authors')
   participants = models.ManyToManyField(User, related_name='participants')

class Problem(models.Model):
   name = models.CharField(max_length=50)
   description = models.CharField(max_length=1000)
   writer = models.ForeignKey("accounts.User", on_delete=models.CASCADE)  
   score = models.PositiveIntegerField(default=100)

class Submission(models.Model):
   submitted_time = models.DateTimeField()
   participant = models.ForeignKey(User, related_name="submissions", on_delete=models.CASCADE)
   problem = models.ForeignKey(Problem, related_name="submissions", on_delete=models.CASCADE)
   code = models.URLField(max_length=200)
   score = models.PositiveIntegerField(default=0)

I tried following code but I get wrong answer. How can I sort my QuerySet?

def list_users_solved_problem(contest_id, problem_id):
    problem_score = Problem.objects.get(id=problem_id).score

    successful_submissions_ids = Submission.objects.filter(
    Q(score=problem_score) & Q(problem__id=problem_id)).
    values_list('participant__id', flat=True)

    return Contest.objects.get(id=contest_id).
    participants.filter(id__in=successful_submissions_ids).
    order_by('submissions__submitted_time')


Asked By: mahdi5505

||

Answers:

You can .filter(…) [Django-doc] with:

from django.db.models import F

User.objects.filter(
    participants=contest_id,
    submissions__problem_id=problem_id,
    submissions__score=F('submissions__problem__score')
).order_by('submissions__submitted_time')

The modeling looks however "strange": the submission is not linked to the contest. So if two (or more) contests share the same problem, then that means it will take the first complete submission to that problem, regardless what the contest is.


Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Answered By: Willem Van Onsem