Reputation: 886
I set up a mapping...
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session
from sqlalchemy.ext.hybrid import hybrid_property
Base = declarative_base()
class Chap(Base):
__tablename__ = 'chap'
id = Column(Integer, primary_key=True)
tophats = Column(Integer)
exclamation = Column(String)
@hybrid_property
def hat_brag(self):
return "I have "+str(self.tophats)+" tophats!"
@hybrid_property
def cold_complain(self):
return self.exclamation+", it's cold!"
...and I created a chap.
>>>c1 = Chap(tophats=5, exclamation="Blimey")
Now I want to query this chap based on how he complains about the weather:
>>>print(Session().query(Chap).filter(Chap.cold_complain == "Blimey, it's cold!"))
SELECT chap.id AS chap_id, chap.tophats AS chap_tophats, chap.gloves AS chap_gloves, chap.exclamation AS chap_exclamation
FROM chap
WHERE chap.exclamation || :exclamation_1 = :param_1
That SQL code looks right, but now I want to query him on another hybrid_property, one that contains an int
converted to a str
...
>>>print(Session().query(Chap).filter(Chap.hat_brag == "I have 5 tophats!"))
SELECT chap.id AS chap_id, chap.tophats AS chap_tophats, chap.exclamation AS chap_exclamation
FROM chap
WHERE false
WHERE false. That doesn't look like the right query! Anyone know what's going on?
Upvotes: 3
Views: 4182
Reputation: 1121484
In a query, SQLAlchemy will use the getter method of a hybrid property without a dedicated @...expression()
method to produce the necessary SQL objects used for a query. The getter method is bound to the class, not an instance, so self
in this scenario will reference your Chap
class.
For your cold_complain
property, that means Chap.exclamation + ", it's cold!"
is returned, resulting in a chap.exclamation || :exclamation_1
SQL expression; the +
translated to the SQL concatenation operator.
But for your hat_brag
property returns a string; the expression executed is really "I have "+str(Chap.tophats)+" tophats!"
, which becomes 'I have Chap.tophats tophats!'
:
>>> "I have "+str(Chap.tophats)+" tophats!"
'I have Chap.tophats tophats!'
That's a fixed, static string. Next, this is made part of your filter, with == "I have 5 tophats!"
. These two static string values are not equal:
>>> "I have "+str(Chap.tophats)+" tophats!" == "I have 5 tophats!"
False
So that's what is being used in the SQL query sent to the database.
What you want to do here instead is to use the expression()
option and define a SQL-friendly version of your query:
from sqlalchemy.sql.expression import cast
class Chap(Base):
# ...
@hybrid_property
def hat_brag(self):
return "I have "+str(self.tophats)+" tophats!"
@hat_brag.expression
def hat_brag(cls):
return "I have " + cast(cls.tophats, String) + " tophats!"
Now the expression
method is used for queries, and the original function on instances, in Python:
>>> c1.hat_brag
'I have 5 tophats!'
>>> print(Session().query(Chap).filter(Chap.hat_brag == "I have 5 tophats!"))
SELECT chap.id AS chap_id, chap.tophats AS chap_tophats, chap.exclamation AS chap_exclamation
FROM chap
WHERE :param_1 || CAST(chap.tophats AS VARCHAR) || :param_2 = :param_3
Upvotes: 3