django conditionally filtering objects

Well, this is rather old question but for those who would like to do the conditional filtering on one line, here is my approach (Btw, the following code can probably be written in a more generic way):

from django.db.models import Q

def conditional_category_filter(category):
    if category != None:
        return Q(category=category)
    else:
        return Q() #Dummy filter

user = User.objects.get(pk=1)
category = Category.objects.get(pk=1)
todays_items = Item.objects.filter(conditional_category_filter(category), user=user, date=now())

The only thing you need to watch is to use the conditional_category_filter(category) call before the keyword arguments like user=user. For example the following code would throw an error:

todays_items = Item.objects.filter(user=user, date=now(), conditional_category_filter(category))

They are several approach to your issue. One approach is to play with Complex lookups with Q objects

from django.db.models import Q

user = User.objects.get(pk=1)
category = Category.objects.get(pk=1)

f1 = Q( user=user, date=now() )
f_cat_is_none = Q( category__isnull = True )
f_cat_is_not_none = Q( category=category )

todays_items = Item.objects.filter( f1 & ( f_cat_is_none | f_cat_is_not_none ) )

I don't right understand in your answer if this is the query you are looking for, but, with this example you can compose easily your own query.

Edited due OP comment

category__isnull == True means that, in database, the item has not an associated category. Perhaps the query you are looking for is:

from django.db.models import Q

user_pk = 1
category_pk = 1  #some times None

f = Q( user__pk = user_pk, date=now() )
if category_pk is not None:
  f &= Q( category__pk = category_pk )

todays_items = Item.objects.filter( f  )

This is only a code sample, fit it to your requirements. Be careful with single _ and double __.


You can chain queries:

user = User.objects.get(pk=1)
category = Category.objects.get(pk=1)
qs = Item.objects.filter(user=user, date=now())
if category:
    qs = qs.filter(category=category)

As queryset are executed lazily, DB hit will occur only when you display items.

Tags:

Django