pythonmysqlsqlalchemy

What is an elegant way to deal with several optional filters?


I have a query method with several optional filters. I want if I pass some not None value to filter parameters then do a filter, if filter value is None to 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()

I want to avoid the pattern:

if XXX:
    res.filter(XXX=XXX)

Is there a more elegant way? For example, pass various filters as parameters. Or maybe 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.