Tengo una clase base declarativa 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) También tengo una función f(title) , que obtiene una cadena y devuelve una de las 3 variantes de cadenas: 'bueno', 'tal vez' o 'nunca'. Intento obtener filas filtradas:
rows = s.query(News).filter(News.label == None and f(News.title) == 'good').all()Pero el programa falla y genera 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 and or . Puede influir en ellos hasta cierto punto con__bool__ en Python 3 y __nonzero__ en Python 2, pero todo lo que hace es quedefine el valor de verdad de su objeto .
Si los objetos en cuestión no hubieran implementado __bool__ y arrojado el error, o la implementación no hubiera arrojado, posiblemente habría recibido errores bastante crípticos debido a la naturaleza de cortocircuito de 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]: Trueporque
In [26]: bool(News.label == 'asdf') Out[26]: FalseEsto podría dar lugar a tirones de pelo 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, utilice las funciones de expresión sql and_() , or_() y not_() , o el binario & , | , y ~ sobrecargas del 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 múltiples criterios a una llamada a Query.filter() :
filter(News.label == None, f(News.title) == 'good') o combine múltiples llamadas a filter() :
filter(News.label == None).filter(f(News.title) == 'good')