0

I am trying to filter by the com_proc variable but it does not work... The complexity here is that the filter can contain more than once the same object. Here is my code:

class Act(models.Model):
    com_proc=models.CharField(max_length=100, blank=True, null=True, default=None)
    cs_1=models.ForeignKey(CodeSect, related_name='code_sect_1', blank=True, null=True, default=None)
    cs_2=models.ForeignKey(CodeSect, related_name='code_sect_2', blank=True, null=True, default=None)
    cs_3=models.ForeignKey(CodeSect, related_name='code_sect_3', blank=True, null=True, default=None)

class ActIds(models.Model):
    propos_origine=models.CharField(max_length=4, blank=True, null=True, default=None)
    act=models.ForeignKey(Act)


qs_acts=ActIds.objects.none()
len(qs_acts)
for act in ActIds.objects.all():
    #loop over each cs variable
    for nb in range(1,4):
        code_sect=getattr(act.act, "cs_"+str(nb))
        if code_sect is not None:
            qs_acts._result_cache.append(act)
print "qs_acts"
for act in qs_acts:
    print act.pk, act.act.com_proc, act.propos_origine

I use a dynamic queryset with _result_cache because each object can be present in the filter up to three times (once for each cs variable). I don't know how to reproduce that with a normal filtering.

Output:

9429 Oral procedure COM
9429 Oral procedure COM
9697 Written procedure COM
12352 Written procedure COM
12362 Oral procedure COM

Code:

...
print filter_vars_periods

Output:

{'act__com_proc': 'Written procedure', 'propos_origine': 'COM', 'act__adopt_conseil__gte': datetime.date(2009, 12, 1), 'act__adopt_conseil__lte': datetime.date(2013, 12, 31)}

Code:

temp_filter=qs_acts.filter(**filter_vars_periods)
for act in temp_filter:
    print act.pk, act.act.com_proc, act.propos_origine

Output:

9429 Oral procedure COM
9429 Oral procedure COM
9697 Written procedure COM
12352 Written procedure COM
12362 Oral procedure COM

You can see that the filtering by the com_proc="Written procedure" did not work, I get the same queryset at the end. Why?

Edit

I think I know why two successive filterings do not give the expected output: Chaining multiple filter() in Django, is this a bug? It is said that using two filters is not the equivalent of the logical AND but is the equivalent of the logical OR. So that could be the reason why my second filter doesn't filter anything... Anyway, I still don't know how to solve my problem :(.

Community
  • 1
  • 1
rom
  • 3,592
  • 7
  • 41
  • 71
  • The chained multiple filter link you added is not related to your issue. That functionality only comes into play when you're traversing a 1 to many relationship (or many to many). If you were trying to fetch Acts based on properties of ActId's then it would be of a concern to you. – schillingt Nov 14 '14 at 13:06

1 Answers1

0

I think it's not possible to use a queryset storing more than once the same object. Instead, I use a list:

class Act(models.Model):
    com_proc=models.CharField(max_length=100, blank=True, null=True, default=None)
    cs_1=models.ForeignKey(CodeSect, related_name='code_sect_1', blank=True, null=True, default=None)
    cs_2=models.ForeignKey(CodeSect, related_name='code_sect_2', blank=True, null=True, default=None)
    cs_3=models.ForeignKey(CodeSect, related_name='code_sect_3', blank=True, null=True, default=None)

class ActIds(models.Model):
    propos_origine=models.CharField(max_length=4, blank=True, null=True, default=None)
    act=models.ForeignKey(Act)


qs_acts=[]
for act in ActIds.objects.all():
    #loop over each cs variable
    for nb in range(1,4):
        code_sect=getattr(act.act, "cs_"+str(nb))
        if code_sect is not None:
            qs_acts.append(act)

Then the problem is that I cannot do the following anymore:

temp_filter=qs_acts.filter(**filter_vars).exclude(**exclude_vars)

Instead I created a function to translate django syntax:

def filter_exclude_list(list_acts, filter_vars={}, exclude_vars={}):
    list_acts_new=[]
    for act in list_acts:
        ok=True
        for key, value in filter_vars.iteritems():
            #related object: "act__validated_attendance":1
            if key[:5]=="act__":
                key=key[5:]
                instance=act.act
            else:
                instance=act
            #greater than or equal: "nb_point_a__gte": 1
            if key[-5:]=="__gte":
                if getattr(instance, key[:-5])<value:
                    ok=False
                    break
            #"com_amdt_tabled__isnull": False
            elif key[-8:]=="__isnull":
                if getattr(instance, key[:-8]) is None:
                    ok=False
                    break
            #equal to: "nb_point_a": 1
            elif getattr(instance, key) != value:
                ok=False
                break

        if ok:
            for key, value in exclude_vars.iteritems():
                #"adopt_cs_abs": None
                if key=="adopt_cs_abs" and value is None:
                    if not getattr(instance, key).exists():
                        ok=False
                        break
                #different from: "nb_point_a": 1
                elif getattr(instance, key) == value:
                    ok=False
                    break

            if ok:
                list_acts_new.append(act)

    return list_acts_new

Then I can do:

qs_acts_new=filter_exclude_list(qs_acts, filter_vars=filter_vars, exclude_vars=exclude_vars)

This works, but I am posting this hoping that someone has a better and more concise solution :).

rom
  • 3,592
  • 7
  • 41
  • 71