[Django]-How to add if statement in filter?

7๐Ÿ‘

โœ…

You can build a dictionary and then pass it as keyword parameters using the double-star syntax f(**kwargs):

conds = {}
if direction != '': conds["strdir"] = direction
if strtype != '': conds["strtype"] = strtype
if province != '': conds["province"] = province
if city != '': conds["city"] = city
queryset = address.objects.filter(addline__startswith=keyword, 
                                  **conds)[:10]
if not queryset.exists():
    queryset = address.objects.filter(strname__startswith=keyword, 
                                      **conds)[:10]
๐Ÿ‘ค6502

1๐Ÿ‘

There are 16 posibilities, it means i need to write 16 if statements!

No!, not if these parameters act more or less independent. We can for example first abstract common logic away:

def add_custom_filters(qs, direction, strtype, city, province):
    if direction:
        qs = qs.filter(strdir=direction)
    if strtype:
        qs = qs.filter(strype=strtype)
    if city:
        qs = qs.filter(strcity=city)
    if privince:
        qs = qs.filter(strprov=privince)
    return qs

(might need some altering)

So now we can use this logic like:

queryset = address.objects.filter(addline__startswith=keyword)
queryset = add_custom_filters(queryset, direction, strtype, city, province)[:10]
if not queryset:
    queryset = address.objects.filter(strname__startswith=keyword)
    queryset = add_custom_filters(queryset, direction, strtype, city, province)[:10]
return queryset

We thus only need four if cases, and we reuse this function for the two-attempt approach.

Since filtering if not truthiness is True is a common pattern, we can encapsulate this in a helper function:

def filter_if_truthfull(qs, **kwargs):
    retrurn qs.filter(**{k: v for k, v in kwargs.items() if v})

then we can use it like:

queryset = address.objects.filter(addline__startswith=keyword)
queryset = filter_if_truthfull(queryset, strdir=direction, strtype=strtype, strcity=city, strprov=province)[:10]
if not queryset:
    queryset = address.objects.filter(strname__startswith=keyword)
    queryset = filter_if_truthfull(queryset, strdir=direction, strtype=strtype, strcity=city, strprov=province)[:10]
return queryset

This allows us to add an arbitrary amount of named filter criteria that are only applied in case the value has truthiness true (for a string, this happens if the string not empty, in case it is None, it is not a string, but these filters are also not considered).

In case you are going to use the results of a QuerySet anyway, it is better to check if queryset, since this will perform a query, that loads the elements into the queryset as well, whereas .exists() will query with an EXISTS query, and if you want to process the elements later, you need to perform an extra query to fetch them in memory.

Leave a comment