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.