Django filter using Q and multiple fields with different values

Issue

I am trying to generate a result that satisfies with the filter query below:

indicators = request.GET.getlist('indicators[]')
fmrprofiles = FMRPriority.objects.all()
q_objects = Q()
obj_filters = []
for indicator in indicators:
    split_i = indicator.split('_')
    if len(split_i) == 5:
        if not any(d['indicator'] ==  split_i[1] for d in obj_filters):
            obj_filters.append({
                'indicator': split_i[1],
                'scores': []
            })

        for o in obj_filters:
            if split_i[1] == o['indicator']:
                o['scores'].append(int(split_i[4]))

for obj in obj_filters:
    print (obj['scores'])
    q_objects.add(Q(pcindicator__id = int(obj['indicator'])) & Q(score__in=obj['scores']), Q.AND)
print (q_objects)
fmrprofiles = fmrprofiles.values('fmr__id','fmr__road_name').filter(q_objects).order_by('-fmr__date_validated')
print (fmrprofiles.query)

Basically, indicators is a list e.g. ['indicator_1_scoring_1_5', 'indicator_1_scoring_1_4', 'indicator_2_scoring_2_5']

I wanted to filter FMRPriority with these following fields:

  • pcindicator
  • score

e.g. pcindicator is equal 1 and scores selected are 5,4..another selection pcindicator is equal to 2 and scores selected are 3.
The query q_objects.add(Q(pcindicator__id = int(obj['indicator'])) & Q(score__in=obj['scores']), Q.AND) returns empty set..i have tried also the raw sql, same result.

Model:

class FMRPriority(models.Model):

    fmr = models.ForeignKey(FMRProfile, verbose_name=_("FMR Project"), on_delete=models.CASCADE)
    pcindicator = models.ForeignKey(PCIndicator, verbose_name=_("Priority Indicator"), on_delete=models.PROTECT)
    score = models.FloatField(_("Score"))

Solution

I solve this by using OR and count the occurrence of id then exclude those are not equal to the length of filters:

for obj in obj_filters:
    print (obj['scores'])
    q_objects.add(
        (Q(fmrpriority__pcindicator__id = int(obj['indicator'])) & Q(fmrpriority__score__in=obj['scores'])), Q.OR
    )

fmrprofiles = fmrprofiles.values(*vals_to_display).filter(q_objects).annotate(
        num_ins=Count('id'),
        ...
    )).exclude(
        ~Q(num_ins = len(obj_filters))
    ).order_by('rank','road_name') 

Answered By – Sachi Tekina

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published