Обзор проблемы
С учетом моделей
class Candidate(BaseModel):
name = models.CharField(max_length=128)
class Status(BaseModel):
name = models.CharField(max_length=128)
class StatusChange(BaseModel):
candidate = models.ForeignKey("Candidate", related_name="status_changes")
status = models.ForeignKey("Status", related_name="status_changes")
created_at = models.DateTimeField(auto_now_add=True, blank=True)
И SQL Таблицы:
candidates
+----+--------------+
| id | name |
+----+--------------+
| 1 | Beth |
| 2 | Mark |
| 3 | Mike |
| 4 | Ryan |
+----+--------------+
status
+----+--------------+
| id | name |
+----+--------------+
| 1 | Review |
| 2 | Accepted |
| 3 | Rejected |
+----+--------------+
status_change
+----+--------------+-----------+------------+
| id | candidate_id | status_id | created_at |
+----+--------------+-----------+------------+
| 1 | 1 | 1 | 03-01-2019 |
| 2 | 1 | 2 | 05-01-2019 |
| 4 | 2 | 1 | 01-01-2019 |
| 5 | 3 | 1 | 01-01-2019 |
| 6 | 4 | 3 | 01-01-2019 |
+----+--------------+-----------+------------+
Я хочу получить общее количество кандидатов с данным статусом , но учитывается только последний status_change .
Другими словами, StatusChange используется для отслеживания истории статуса, но только последний считается, когда подсчет текущего статуса кандидатов.
SQL Решение
Используя SQL, я смог добиться этого, используя Group BY и COUNT. (SQL не проверено)
SELECT
status.id as status_id
, status.name as status_name
, COUNT(*) as status_count
FROM
(
SELECT
status_id,
Max(created_at) AS latest_status_change
FROM
status_change
GROUP BY status_id
)
AS last_status_count
INNER JOIN
last_status_count AS status
ON (last_status_count.status_id = status.id)
GROUP BY status.name
ORDER BY status_count DESC;
last_status_count
+-----------+-------------+--------+
| status_id | status_name | count |
+-----------+-------------+--------+
| 1 | Review | 2 | # <= Does not include instance from candidate 1
| 2 | Accepted | 1 | # because status 2 is latest
| 3 | Rejected | 1 |
+-----------+-------------+--------+
Попытка Django Решение
Мне нужно представление, чтобы вернуть каждый статус и соответствующий им счет - например, [{ status_name: "Review", count: 2 }, ...]
Я не уверен, как создать этот набор запросов, не извлекая все записи и не агрегируя в python.
Я подумал, что мне нужно annotate()
и, возможно, Subquery
, но я не смог прошить все это вместе.
Самое близкое, что я получил, это то, что подсчитывает количество изменений статуса для каждого статуса, но подсчитывает не последние изменения.
queryset = Status.objects.all().annotate(case_count=Count("status_changes"))
Я нашел много SO вопросы по агрегации, но я не смог найти четкого ответа по поводу агрегирования и аннотирования "последнее.
Заранее спасибо.