1

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...

David Duran
  • 1,786
  • 1
  • 25
  • 36

0 Answers0