Django ANDing queryset filters using variable assignment

1.3k views Asked by At

Let's say I have a queryset of Products that has tons of prefetch objects in it. Among the prefetch objects are "categories", and categories can have "filters". Not every Product has categories and filters defined.

Example categories could be "price" and "color", and filters could be "lt $5" and "blue".

I can get only the Products that have "price" and "color" defined easily enough, using exclude.

But how do I get this, for example:

All the Products that have "price"=* defined, and all the Products that have "color"="blue" defined.

Any ideas?

EDIT: I'm gonna go ahead and show the solution I came up with. Bear in mind that the example above is very much simplified. There are dozens of categories, each with dozens of filters.

Imagine that the the user chose category 'shoes' but not filters 'shoes': 'women' or 'shoes': 'football'.

And she chose category 'price' AND filter 'price': 'lt $5'

from django.db.models import Q
import operator
argument_list = []
# categories_selected = all the categories that have been checked on the page     
for c in categories_selected: 
# categories_filtered = all the (selected) categories that have filters applied
    if c not in categories_filtered:
        # Here we get all where <category>_id is defined
        argument_list.append(Q(**{c+'__id__isnull': False}))
    else:
        for f in get_category_filters_from_url(filters_selected, c):
        # get_category_filters_from_url() returns all the filters (by id) belonging 
        # to a given category, e.g., 'price': 'lt $5'
            argument_list.append(Q(**{c+'__id': f}))

resources = resources.filter(reduce(operator.or_, argument_list)).distinct()

I hope that's clear enough, and that it helps someone else muddle through something like this.

1

There are 1 answers

4
Caumons On BEST ANSWER

Without knowing how your models look like, I'd use the following query:

Query with AND:

Product.objects.filter(categories__price__isnull=False, categories__color='blue')

Query with OR:

from django.db.models import Q

Product.objects.filter(Q(categories__price__isnull=False) | Q(categories__color='blue'))