Model
I have a model in Django, which has several fields, but, for the purpose of this question, it can be summarized with the following fields:
from django.db import models
from django.core.validators import MaxValueValidator, MinValueValidator
class FailureTable(models.Model):
DETECTION_CHOICES = (
('P', 'PBIT'),
('C', 'CBIT'),
)
failure_id = models.CharField(max_length=50, blank=False, null=False, default='Failure ID', verbose_name='Failure ID')
end_effect = models.CharField(max_length=200, blank=False, null=False, default='No safety Effect', verbose_name='End Effect')
detection = models.CharField(max_length=1, choices=DETECTION_CHOICES, blank=True, null=True, verbose_name='Detection')
failure_rate = models.FloatField(blank=True, null=True, validators=[MinValueValidator(0), MaxValueValidator(1)], verbose_name='Failure Rate')
Data example
A typical kind of table would something like this:
failure_id | end_effect | detection | failure_rate
==========================================================
FM_01 | loss of function | P | 1e-6
FM_01 | erroneous function | P | 1e-6
FM_02 | loss of function | P | 1e-6
FM_03 | loss of function | C | 2e-6
Current code
Currently, I have been able to obtain a dictionary that sums all the failure rates according to the detection type with the following annotate
expression:
from django.db.models import Sum
dict_fr={item["detection"]: '%.2E' % item["sum_fr"] for item in FailureTable.objects.filter.values("detection").annotate(sum_fr=Sum('failure_rate'))}
Problem
The problem is that this sums the failure_rate regardless of the "failure_id" field. I would like not to sum the failure_rate when we have already considered a failure_id. In the example that I have provided, what I would like is:
dict_fr={"P": 2e-6, "C": 2e-6}
However, what I get with the current annotate
expression is:
dict_fr={"P": 3e-6, "C": 2e-6}
Question
So, my question is how could I use annotate
to sum only the entries with a distinct field "failure_id"? This field is not used neither to classify the sum (values
) nor to do the actual sum. Could it be done without building a loop (only database query)?
Note that the entries with the same failure_id
have the same failure_rate
.
Things I tried
I have tried using distinct
without success:
NotImplementedError: annotate() + distinct(fields) is not implemented.
Some related questions are How to add an annotation on distinct items?, Django: Using Annotate, Count and Distinct on a Queryset, Django annotate count with a distinct field. However, they make the distinct
on the same field that is Summing or Counting, not on a third one...
Solution using a for-loop
I have been able to obtain the desired result using a mixture of annotate
and a for-loop:
dict_fr = {}
dict_FM_ids = {}
for item in FailureTable.objects.values("detection", "failure_id", "end_effect").annotate(sum_fr=Sum('failure_rate')):
# Get variables
failure_id = item["failure_id"]
detection = item["detection"]
sum_fr = item["sum_fr"]
# Add to dictionaries
if failure_id not in dict_FM_ids:
# Case where you can sum
dict_FM_ids[failure_id]=None
if detection in dict_fr:
dict_fr[detection] += sum_fr
else:
dict_fr[detection] = sum_fr
However, it would be better if I could achieve the result with only one query...