Impact
On the group detail page, file_rows_for_group() runs one extra COUNT query per audit file. audit_files_for_group() prefetches events__group, but file_rows_for_group() then calls audit_file.events.filter(group=group).count() — and .filter() on a prefetched relation does not use the prefetch cache, so it issues a fresh query for every file. A group with N audit files therefore does N+1 queries just for the per-file event counts.
Code pointers
forensics/analysis.py:102 — "group_event_count": audit_file.events.filter(group=group).count() (the N+1).
forensics/analysis.py:42 — audit_files_for_group() already prefetch_related("events__group"), which this defeats.
Expected behavior
Per-file, per-group event counts should be computed without a query per file.
Suggested fix
- Annotate the count in the queryset, e.g.
audit_files_for_group(group) adds .annotate(group_event_count=Count("events", filter=Q(events__group=group))), or
- Count in Python from the already-prefetched
events.all() (filtering by event.group_id == group.id in memory) instead of issuing .filter().count().
- Add a query-count assertion for the group detail view.
Impact
On the group detail page,
file_rows_for_group()runs one extraCOUNTquery per audit file.audit_files_for_group()prefetchesevents__group, butfile_rows_for_group()then callsaudit_file.events.filter(group=group).count()— and.filter()on a prefetched relation does not use the prefetch cache, so it issues a fresh query for every file. A group with N audit files therefore does N+1 queries just for the per-file event counts.Code pointers
forensics/analysis.py:102—"group_event_count": audit_file.events.filter(group=group).count()(the N+1).forensics/analysis.py:42—audit_files_for_group()alreadyprefetch_related("events__group"), which this defeats.Expected behavior
Per-file, per-group event counts should be computed without a query per file.
Suggested fix
audit_files_for_group(group)adds.annotate(group_event_count=Count("events", filter=Q(events__group=group))), orevents.all()(filtering byevent.group_id == group.idin memory) instead of issuing.filter().count().