1

I have three models as below:

class Person(models.Model):
    first_name = models.CharField(max_length=20)
    last_name = models.CharField(max_length=20)

class PersonSession(models.Model):
    start_time = models.DateTimeField(auto_now_add=True)
    end_time = models.DateTimeField(null=True,
                                    blank=True)
    person = models.ForeignKey(Person, related_name='sessions')

class GameSession(models.Model):
    score = models.PositiveIntegerFeild()
    person_session = models.ForeignKey(PersonSession, related_name='games')

Now I want get list of persons with calculated all scores they get plus the time they spend in all their game sessions and branch sessions, the query I currently use is as below:

Person.objects.annotate(game_score=Sum(Case(When(Q(sessions__games__isnull=True),
                                       then=0),
                                  default=F('sessions__games__score'),
                                  output=models.PositiveIntegerField())))\
    .annotate(spent_time=Sum(Case(When(Q(branch_sessions__isnull=False) &
                                       Q(branch_sessions__end_time__isnull=False),
                                       then=ExpressionWrapper(ExtractHour(ExpressionWrapper(F('sessions__end_time')-F('sessions__start_time'),
                                                                                            output_field=models.TimeField()),
                                                                          output=models.PositiveIntegerField()) * 60 + \
                                                              ExtractMinute(ExpressionWrapper(F('sessions__end_time')-F('sessions__start_time'),
                                                                                              output_field=models.TimeField()),
                                                                            output=models.PositiveIntegerField()),
                                                              output_field=models.PositiveIntegerField())),
                                  default=0,
                                  output=models.PositiveIntegerField()))) \
    .annotate(total_score=F('game_score') + F('spent_time') * SCORE_MIN)\
    .order_by('-total_score')

The problem is that this query join all tables together so for each PersonSession may be we have many GameSession and the resulting table contains the repeated row of a person session which causes time of a person sessions sums together repeatedly and generates a wrong result.

In a sql query I must first construct a subtable with sum of game socres and then join that table with persons session, but I do not know how could this possible in django?

I am using django 1.11 and postgres.

motam
  • 677
  • 1
  • 6
  • 24
  • Using [subquery expression](https://docs.djangoproject.com/en/2.0/ref/models/expressions/#subquery-expressions) I could get the result that I wanted but still I think this is not an efficient solution and using a subtable and a join could be a better solution. – motam May 20 '18 at 05:44

0 Answers0