views:

80

answers:

2

I've got a model like this:

class Thing(models.Model):
    property1 = models.IntegerField()
    property2 = models.IntegerField()
    property3 = models.IntegerField()

class Subthing(models.Model):
    subproperty = models.IntegerField()
    thing = modelsForeignkey(Thing)
    main = models.BooleanField()

I've got a function that is passed a list of filters where each filter is of the form {'type':something, 'value':x}. This function needs to return a set of results ANDing all the filters together:

final_q = Q()
for filter in filters:
        q = None
        if filter['type'] =='thing-property1':
            q = Q(property1=filter['value'])
        elif filter['type'] =='thing-property2':
            q = Q(property2=filter['value'])
        elif filter['type'] =='thing-property2':
            q = Q(property3=filter['value'])
        if q:
            final_q = final_q & q
return Thing.objects.filter(final_q).distinct()

Each Subthing has a Boolean property 'main'. Every Thing has 1 and only 1 Subthing where main==True.

I now need to add filter that returns all the Things which have a Subthing where main==True and subproperty==filter['value']

Can I do this as part of the Q object I'm constructing? If not how else? The queryset I get before my new filter can be quite large so I would like a method that doesn't involve looping over the results.

+1  A: 

Using (instead of final_q=Q() in the beginning)

final_q=Q(subthing_set__main=True)
sub_vals = map(lambda v: v['value'], filters)
if sub_vals:
    final_q = final_q & Q(subthing_set__subproperty__in=sub_vals)

should get you what you want, you can also adjust your loop to build the sub_vals list and apply it after the loop.

subthing_set is and automatically added related field added to the Thing to access related Subthings.

you can assign another related name, e.g.

thing=models.ForeignKey(Thing,related_name='subthings')
Evgeny
+1  A: 

It's a bit easier to understand if you explicitly give your Subthings a "related_name" in their relationship to the Thing

class Subthing(models.Model):
    ...
    thing = models.ForeignKey(Thing, related_name='subthings')
    ...

Now, you use Django join syntax to build your Q object:

Q(subthings__main=True) & Q(subthings__subproperty=filter['value'])

The reverse relationship has the default name 'subthing_set', but I find that it's easier to follow if you give it a better name like 'subthings'.

Joe Holloway
Doh! For some reason I was looking for a much more complicated solution than that! This seems to work perfectly.
andybak