SQLAlchemy: resultados inesperados al usar `and` y` o`

Tengo una base de datos “Noticias” creada a través de SQLAlchemy:

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) 

También tengo una función f(title) , que obtiene una cadena y devuelve una de las 3 variantes de cadenas: “buena”, “quizás” o “nunca”. Intento conseguir filas filtradas:

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

Pero el progtwig falla, generando este error:

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

¿Cómo puedo resolverlo?

El problema es este:

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

Python no permite anular el comportamiento de las operaciones booleanas and y or y. Puedes influir en ellos hasta cierto punto con __bool__ en Python 3 y __nonzero__ en Python 2, pero todo lo que hace es que define el valor de verdad de tu objeto .

Si los objetos en cuestión no hubieran implementado __bool__ y __bool__ arrojado el error, o si la implementación no se hubiera lanzado, posiblemente habrían obtenido errores bastante crípticos debido a la naturaleza de cortocircuito de and y or :

 In [19]: (News.label == 'asdf') and True Out[19]:  In [24]: (News.label == 'asdf') or True Out[24]: True 

porque

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

Esto podría y conduciría a que el cabello se dispare en forma de expresiones SQL incorrectas:

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

Para producir expresiones SQL booleanas, use las funciones de expresión sql and_() , or_() , y not_() , o el binario & , | y ~ sobrecargas de operador:

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

o

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

o pase varios criterios a una llamada a Query.filter() :

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

o combinar múltiples llamadas para filter() :

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