- changed status to resolved
row_processor is None when querying with hybrid_property
Issue #3448
resolved
I'm using a hybrid_property to map a column containing int identifiers to the un-persisted python objects they identify. We're writing queries that compare that column with those python objects. In this example, I'm mapping an int "id" column to a python IdObject wrapper.
In SQLAlchemy 0.9.9, this example ran with 1 row being returned, but in SQLAlchemy 1.0.5, I get a TypeError (traceback pasted below). Is there a better (working) way to do this?
from sqlalchemy import Table, Column, Integer, MetaData, create_engine
from sqlalchemy.ext.hybrid import hybrid_property, Comparator
from sqlalchemy.orm import mapper, Query, Session
class Entity(object):
pass
class IdObject(object):
def __init__(self, id_):
self.id = id_
def __cmp__(self, other):
if isinstance(other, int):
return cmp(self.id, other)
return cmp(self.id, other.id)
class IdObjComparator(Comparator):
def __init__(self, id_):
if isinstance(id_, (IdObject, IdObjComparator)):
self.id = id_.id
else:
self.id = id_
def operate(self, op, *other, **kwargs):
return op(self.id,
*(IdObjComparator(id_).id
for id_ in other),
**kwargs)
def __clause_element__(self):
return self.id
def test_hybrid_property(url):
engine = create_engine(url)
metadata = MetaData(bind=engine)
table = Table('test_tbl', metadata,
Column('id', Integer, primary_key=True),
Column('value', Integer))
metadata.create_all()
engine.execute(table.insert().values([{'value': 1}]))
mapper(Entity,
table,
properties={'_id': table.c.id},
include_properties=())
hybrid_id = hybrid_property(
lambda zelf: IdObject(zelf._id)
).comparator(lambda klass: IdObjComparator(klass._id))
Entity.id = hybrid_id
query_for_id1 = (Query([Entity.id])
.filter(Entity.id == IdObject(1))
.order_by(Entity.id))
results = list(query_for_id1.with_session(Session(bind=engine)))
assert len(results) == 1
if __name__ == '__main__':
test_hybrid_property('postgresql://rich@/test_hybrid_prop')
Traceback (most recent call last):
File "/Users/rich/Dev/test/test/test_hybrid_property.py", line 63, in <module>
test_hybrid_property('postgresql://rich@/test_hybrid_prop')
File "/Users/rich/Dev/test/test/test_hybrid_property.py", line 58, in test_hybrid_property
results = list(query_for_id1.with_session(Session(bind=engine)))
File "/Users/rich/.virtualenvs/test/lib/python2.7/site-packages/sqlalchemy/orm/loading.py", line 84, in instances
util.raise_from_cause(err)
File "/Users/rich/.virtualenvs/test/lib/python2.7/site-packages/sqlalchemy/util/compat.py", line 199, in raise_from_cause
reraise(type(exception), exception, tb=exc_tb)
File "/Users/rich/.virtualenvs/test/lib/python2.7/site-packages/sqlalchemy/orm/loading.py", line 72, in instances
for row in fetch]
TypeError: 'NoneType' object is not callable
Comments (5)
-
repo owner -
repo owner - changed status to open
it's not what the docs say. fine
-
repo owner - changed status to resolved
- Fixed an unexpected-use regression whereby custom :class:
.Comparator
objects that made use of the__clause_element__()
method and returned an object that was an ORM-mapped :class:.InstrumentedAttribute
and not explicitly a :class:.ColumnElement
would fail to be correctly handled when passed as an expression to :meth:.Session.query
. The logic in 0.9 happened to succeed on this, so this use case is now supported. fixes#3448
→ <<cset e765c55e8cc7>>
-
repo owner thanks for reporting!
-
reporter Thanks for the fast reply! As you said, returning a
ColumnElement
worked perfectly. - Log in to comment
__clause_element__()
has to return aColumnElement
, not the mapped attribute. so when you say "cls.id", that's the InstrumentedAttribute, not the SQL element. change as follows and works in both cases: