Yuchen Huang
Yuchen Huang

Reputation: 311

How to add if statement in filter?

I have 4 parameters: city, province, strtype, direction, when the parameter is not empty I want to use it in filter, when it is empty, do not set it as filter keyword.

The part of current code I use is as below:

if (direction == '') & (strtype == '') & (city == '') & (province == ''):
    queryset = address.objects.filter(addline__startswith=keyword)[:10]
    if not queryset.exists():
        queryset = address.objects.filter(strname__startswith=keyword)[:10]
        return queryset
    else:
        return queryset

if (direction != '') & (strtype == '') & (city == '') & (province == ''):
    queryset = address.objects.filter(addline__startswith=keyword, 
                                      strdir=direction)[:10]
    if not queryset.exists():
        queryset = address.objects.filter(strname__startswith=keyword, 
                                          strdir=direction)[:10]
        return queryset
    else:
        return queryset

There are 16 posibilities, it means i need to write 16 if statements! This is too many codes and ungraceful, is there a terse solution?

Upvotes: 3

Views: 3421

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476557

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.

Upvotes: 1

6502
6502

Reputation: 114461

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]

Upvotes: 7

Related Questions