I have a function that checks an object for some properties and returns boolean values depending on the result. It's too complex to write it in filter, but it works and returns the right value.
Now I want to use sqlalchemy to return all objects that this function returns True for. I tried:
DBSession.query(MyObject).filter(self.check_attributes(MyObject) == True).all()
and
DBSession.query(MyObject).filter(self.check_attributes(MyObject)).all()
Both failed to select the right objects. What am I doing wrong?
The second one, filter_by(), may be used only for filtering by something specifically stated - a string or some number value. So it's usable only for category filtering, not for expression filtering. On the other hand filter() allows using comparison expressions (==, <, >, etc.)
functions is a callable within the sqlalchemy. sql module of the SQLAlchemy project. ClauseElement, Select, column, expression, extract, operators, schema, select, sqltypes, and table are several other callables with code examples from the same sqlalchemy. sql package.
SQLAlchemy is the ORM of choice for working with relational databases in python. The reason why SQLAlchemy is so popular is because it is very simple to implement, helps you develop your code quicker and doesn't require knowledge of SQL to get started.
Lazy loading refers to objects are returned from a query without the related objects loaded at first. When the given collection or reference is first accessed on a particular object, an additional SELECT statement is emitted such that the requested collection is loaded.
As I said in my comment, hybrid_method
/hybrid_property
is the appropriate pattern for your use case. It may at first seem complicated, but it's actually quite simple. The first version of the function operates exactly like a Python method or property, and the second part acts as a class method. SQLAlchemy filters work on the class to generate SQL.
This is just a useless example, but instead of therabouts
your example could have a complicated calculation.
If you don't need to pass any parameters then I suggest using hybrid_property
instead.
class MyObject(Model):
name = Column(String)
num = Column(Integer)
@hybrid_method
def therabouts(self, n):
return self.num > n - 5 and self.num <= n + 5
@therabouts.expression
def therabouts(cls, n):
return and_(cls.num > n - 5, cls.num <= n + 5)
@hybrid_property
def is_al(self):
return self.name.lower().startswith('al')
@is_al.expression
def is_al(cls):
return cls.name.ilike('al%')
# When used as a class method @thereabouts.expression is called
near20 = session.query(MyObject).filter(MyObject.therabouts(20)).first()
# When used as an instance, @hybrid_method is called
near20.therabouts(20) # True
near20.therabouts(22) # Maybe True
near20.therabouts(50) # False
# filter example (class)
all_als = session.query(MyObject).filter(MyObject.is_al).all()
for al in all_als:
print al.name
# output Alan, Alanzo, Albert...
# instance example (self)
bob = MyObject(name='Robert')
print bob.is_al # False
If by "use a function" you mean write the filters inside a function, you certainly can - you just need to pass the correct argument to it (the query) and use its return (the filtered query) in the proper place
Let's take an example from SQLAlchemy's tutorial:
wendy = session.query(User).filter_by(name='wendy').one()
You can move the filter(s) to a function easily:
def my_filter(query):
return query.filter_by(name='wendy')
wendy = my_filter(session.query(User)).one()
However, if by " function that checks an object for some properties and returns boolean values depending on the result" you mean a function that accepts a database record as an argument, I don't think it can be done (without subverting the whole purpose of using SQL).
--EDIT-- As doog abides
points out, the hybrid
extension, while not operating on database records, does something that's equivalent for many practical purposes.
Of course, some people will insist in writing something like:
all_users = session.query(User).all()
wendy= filter( lambda u:u.name=='wendy', all_users )
But, for the sake of the sanity of your fellow programmers and users, please don't do that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With