pythonmysqlcoding-stylesqlalchemy

sqlalchemy - elegant way to deal with several optional filters?


Suppose I have a query method with serveral optional filters. What I want to achieve is, If I pass some not None value to filter parameters then do a filter, if filter value is None, then just ignore it.

def get_query_results(filter1=None, filter2=None, ...):
    res = models.Item.query
    if filter1 is not None:
        res = res.filter(filter1=filter1)
    if filter2 is not None:
        res = res.filter(filter2=filter2)
    ....
    return res.all()

What I want to avoid is the pattern

if XXX:
    res.filter(XXX=XXX)

I wonder if there is any more elegant way to achieve this?

For example, pass various filters as parameters?

Or maybe, we can do some magic to omit the filter when the filter value is None?


Solution

  • Code perfectly equivalent to the one you've shown is:

    def get_query_results(*filters):
        res = models.Item.query
        for i, filt in enumerate(filters, 1):
            if filt is not None:
                d = {'filter{}'.format(i): filt}
                res = res.filter(**d)
        return res.all()
    

    I'm not quite sure why you need the named argument to res.filter to be specifically filter1, filter2, etc, but this snippet will do it without the repetitious pattern that you understandably want to avoid.

    Should the names not actually be filter1, filter2, etc, that's OK as long as the required names are known:

    NAMES = 'foo bar baz bat'.split()
    
    def get_query_results(*filters):
        res = models.Item.query
        for name, filt in zip(NAMES, filters):
            if filt is not None:
                d = {name: filt}
                res = res.filter(**d)
        return res.all()
    

    This variant would work in this case.