Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLAlchemy: unexpected results when using `and` and `or`

Tags:

I have a declarative base class News:

class News(Base):     __tablename__ = "news"     id = Column(Integer, primary_key = True)     title = Column(String)     author = Column(String)     url = Column(String)     comments = Column(Integer)     points = Column(Integer)     label = Column(String) 

I also have a function f(title), that gets a string and returns one of 3 variants of strings: 'good', 'maybe' or 'never'. I try to get filtered rows:

rows = s.query(News).filter(News.label == None and f(News.title) == 'good').all() 

But the program fails, raising this error:

raise TypeError("Boolean value of this clause is not defined") 

How can I resolve it?

like image 220
Mark Seliaev Avatar asked Mar 08 '17 20:03

Mark Seliaev


People also ask

What is the result object in SQLAlchemy?

In SQLAlchemy 1.4 and above, this object is used for ORM results returned by Session.execute (), which can yield instances of ORM mapped objects either individually or within tuple-like rows. Note that the Result object does not deduplicate instances or rows automatically as is the case with the legacy Query object.

What's new in SQLAlchemy?

New in version 1.4: SQLAlchemy now has a transparent query caching system that substantially lowers the Python computational overhead involved in converting SQL statement constructs into SQL strings across both Core and ORM. See the introduction at Transparent SQL Compilation Caching added to All DQL, DML Statements in Core, ORM.

Why is SQLAlchemy so hard to use?

Modern SQLAlchemy usage, especially the ORM, places a heavy stress on working within the context of a transaction at all times; the “implicit execution” concept makes the job of associating statement execution with a particular transaction much more difficult.

What is SQLAlchemy CONNECT method?

method sqlalchemy.engine.Engine.connect(close_with_result=False) ¶ Return a new Connection object. The Connection object is a facade that uses a DBAPI connection internally in order to communicate with the database. This connection is procured from the connection-holding Pool referenced by this Engine.


1 Answers

The problem is this:

News.label == None and f(News.title) == 'good' #                  ^^^ here 

Python does not allow overriding the behaviour of boolean operations and and or. You can influence them to some extent with __bool__ in Python 3 and __nonzero__ in Python 2, but all that does is that it defines the truth value of your object.

If the objects in question had not implemented __bool__ and thrown the error, or the implementation had not thrown, you would've gotten possibly rather cryptic errors due to the short-circuiting nature of and and or:

In [19]: (News.label == 'asdf') and True Out[19]: <sqlalchemy.sql.elements.BinaryExpression object at 0x7f62c416fa58>  In [24]: (News.label == 'asdf') or True Out[24]: True 

because

In [26]: bool(News.label == 'asdf') Out[26]: False 

This could and would lead to hair pulling in the form of incorrect SQL expressions:

In [28]: print(News.label == 'asdf' or News.author == 'NOT WHAT YOU EXPECTED') news.author = :author_1 

To produce boolean SQL expressions either use the and_(), or_(), and not_() sql expression functions, or the binary &, |, and ~ operator overloads:

# Parentheses required due to operator precedence filter((News.label == None) & (f(News.title) == 'good')) 

or

filter(and_(News.label == None, f(News.title) == 'good')) 

or pass multiple criterion to a call to Query.filter():

filter(News.label == None, f(News.title) == 'good') 

or combine multiple calls to filter():

filter(News.label == None).filter(f(News.title) == 'good') 
like image 105
Ilja Everilä Avatar answered Oct 05 '22 12:10

Ilja Everilä