Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use a function in an SQLAlchemy filter?

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?

like image 444
user3146472 Avatar asked Dec 30 '14 14:12

user3146472


People also ask

What is difference between filter and filter by in SQLAlchemy?

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.)

What is func in SQLAlchemy?

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.

Is SQLAlchemy worth using?

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.

What is lazy SQLAlchemy?

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.


2 Answers

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
like image 109
doog abides Avatar answered Oct 06 '22 18:10

doog abides


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.

like image 24
loopbackbee Avatar answered Oct 06 '22 19:10

loopbackbee