How can I reuse a SQLAlchemy filter?

Question:

I have a class which interacts with the SQLAlchemy objects through various methods and it issues queries for various tasks. Some of these queries use the same filter for example:

def get_all_expired(self,my_model):

    q = session.query(my_model).filter(
        my_model.status == 'OK',
        my_model.is_validated == True,
        my_model.expires_at <= plum_dt.now(),
    )

    return q.all()

def update_all_expired(self,my_model):

    session.query(my_model).filter(
        my_model.status == 'OK',
        my_model.is_validated == True,
        my_model.expires_at <= plum_dt.now(),
    ).update({'status':'EXPIRED'})

the query part for these two methods is the same and in my case it is used in other methods as well. I also want to pass different models and get the same queries run on them. Is it possible to create a single re-usable filter so that it is managed in one place and re-used in each query that needs it?

Asked By: KZiovas

||

Answers:

How aboot this?

expired_filter = session.query(MyModel).filter(
        MyModel.status == 'OK',
        MyModel.is_validated == True,
        MyModel.expires_at <= plum_dt.now()
    )
    
    def get_all_expired(self):
        return expired_filter.all()
    
    def update_all_expired(self):
        expired_filter.update({'status':'EXPIRED'

})
Answered By: luke jon

Ok so this is not very well documented, in the official documentation I did not find any examples, or even a mention of the fact that you can unpack multiple filters from a list (maybe they assume it is obvious, who knows).

But the following can be done. Make and unpack a list with the filters, for example:

def filters(self):
    return [my_model.status == 'OK', my_model.is_validated == True,  my_model.expires_at <= plum_dt.now()]

def get_all_expired(self,my_model):

    q = session.query(my_model).filter(*self.filters)

    return q.all()

def update_all_expired(self,my_model):

    session.query(my_model).filter(*self.filters).update({'status':'EXPIRED'})

I found this from a discussion on a github ticket about extending this feature to be able to pass arguments with or condition as well (at the moment the list is read in as having and logic) and also from another example here (although this one has a mistake on how to unpack lists)

Answered By: KZiovas