Django ORM filter by group with where clause

У меня есть модель студентов и мнений. Каждый студент может изменить свое мнение с течением времени. В конечном итоге я хочу построить график, показывающий количество студентов с каждым мнением в определенный день, но в качестве первого шага я хочу подсчитать количество студентов с каждым мнением в определенный день

Моя модель выглядит следующим образом (сокращенно для краткости):

class Student(models.Model):
    first_name = models.CharField(max_length=30, null=True, blank=True)
    surname = models.CharField(max_length=30, null=True, blank=True)


class Opinion(models.Model):
    student = models.ForeignKey('Student', on_delete=models.CASCADE,null=True)

    opdate = models.DateField(null=True, blank=True)
    sentiment_choice = [
    ('Positive', 'Positive'),
    ('Negative', 'Negative'),
    ]   
    sentiment = models.CharField(
        max_length=40,
        choices=sentiment_choice,
        default="Positive",
        null=True, blank=True
    )           

Мой подход заключается в том, чтобы перебрать все даты в диапазоне, отфильтровать таблицу мнений, чтобы получить все данные до этой даты, найти последнее мнение по каждому студенту, подсчитать их и загрузить результаты в массив.

Я знаю, как фильтровать таблицу мнений следующим образом (где start_date - мой итератор):

Opinion.objects.filter(opdate__lte=start_date)

Я также знаю, как подобрать последнее мнение для каждого студента:

 Opinion.objects.values('student').annotate(latest_date=Max('opdate'))

Как мне объединить это, чтобы я мог получить последнее мнение для каждого студента, который находится перед моим итератором?

Я работаю на Django 3.2.12 с SQL Lite DB

Вы можете использовать Subquery выражение [Django-doc] с:

from django.db.models import OuterRef, Subquery

Student.objects.annotate(
    last_sentiment=Subquery(
        Opinion.objects.filter(
            student_id=OuterRef('pk')
        ).order_by('-opdate').values('sentiment')[:1]
    )
)

У Student будет дополнительный атрибут .last_sentiment, который будет содержать sentiment последнюю связанную Opinion запись, или NULL/None, если нет связанной Opinion.

Вернуться на верх