On 9/15/15 2:47 PM, Katie Wurman wrote:
Ok thanks! Adding a bind_expression to my CastToInteger type ensures
that when Person.id is included in a WHERE clause, the param is cast
to varchar.
This is ok, except now I have the following situation:
class CastToIntegerType(types.TypeDecorator):
impl = types.Numeric
def column_expression(self, col):
return func.cast(col, Integer)
def bind_expression(self,col):
return func.cast(col, String)
I think the "impl" here probably needs to be String, since "impl" is to
represent the actual underlying datatype in the DB.
class Person(Base):
__tablename__ = 'person'
id = Column('id_string', CastToIntegerType, primary_key=True)
pets = relationship('Pets',
primaryjoin='foreign(Pets.person_id)==Person.id')
class Pets(Base):
__tablename__ = 'pets'
id = Column('id', Integer, primary_key=True)
person_id = Column('person_id', Integer, ForeignKey('Person.id'),
primary_key=True)
In this case, when I query for person.pets the join condition between
Person and Pets becomes: public.pets.person_id = CAST(%(param_1)s AS
VARCHAR)
pets.person_id is persisted as an Integer, so I would think a comparison
of pets.person_id to an integer value should be using Integer as the
type? Let's check...oh well, it does. It's because that particular
comparison is generated on a column-by-column basis. That's now bug
https://bitbucket.org/zzzeek/sqlalchemy/issues/3530/bind-param-replacement-in-join_condition.
So I'd stick a type_coerce in there and be done with it, which won't
generate the CAST either, if that was the problem....except....that
doesn't seem to be working either because of the way type_coerce doesn't
create a permanent construct, so that's bug
https://bitbucket.org/zzzeek/sqlalchemy/issues/3531/bind-param-replacement-in-join_condition.
Workaround now is at the bottom of 3531 which looks like:
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql import ColumnElement
from sqlalchemy.sql.elements import _clone
class LateTypeCoerce(ColumnElement):
def __init__(self, element, type_):
self.element = element
self.type = type_
self._proxies = [element]
def self_group(self, against=None):
return LateTypeCoerce(self.element.self_group(against=against),
self.type)
def get_children(self, **kwargs):
return self.element,
def _copy_internals(self, clone=_clone, **kw):
self.element = clone(self.element, **kw)
@property
def _from_objects(self):
return self.element._from_objects
def _make_proxy(self, selectable, **kw):
return self.element._make_proxy(selectable, **kw)
then in mapping:
class Person(Base):
__tablename__ = 'person'
id = Column('id_string', CastToIntegerType, primary_key=True)
pets = relationship('Pets',
primaryjoin=lambda: foreign(Pets.person_id) ==
LateTypeCoerce(Person.id, Integer))
I can amend this by explicitly casting Person.id to Integer in the
primaryjoin expression, but that's a little redundant. Is there a
cleaner way to do this?
Thanks!
On Tuesday, September 15, 2015 at 10:49:51 AM UTC-7, Michael Bayer wrote:
On 9/15/15 1:19 PM, Katie Wurman wrote:
Hi,
I'm having trouble implementing a model whose 'id' column needs
to be cast to Integer type. Below is the implementation I've got
so far:
class CastToIntegerType(types.TypeDecorator):
'''
Converts stored String values to Integer via CAST operation
'''
impl = types.Numeric
def column_expression(self, col):
return func.cast(col, Integer)
class Person(Base):
__tablename__ = "person"
id = Column('id_string', CastToIntegerType, primary_key=True)
Then when I run the query
>> person = Person.query.get(12345)
I see the following error:
sqlalchemy.exc.ProgrammingError: (ProgrammingError) operator does
not exist: text = integer
LINE 3: WHERE public.person.id_string = 12345
^
HINT: No operator matches the given name and argument type(s).
You might need to add explicit type casts.
Any ideas what I might be doing wrong?
OK, this is close, column_expression applies to how the column
list in the SELECT renders, e.g. "SELECT CAST(mycolumn, INT) as
mycolumn", for a SQL expression that wraps around a value you're
sending into the WHERE in a comparison expression, you'd use
bind_expression for that:
http://docs.sqlalchemy.org/en/rel_1_0/core/type_api.html#sqlalchemy.types.TypeEngine.bind_expression
<http://docs.sqlalchemy.org/en/rel_1_0/core/type_api.html#sqlalchemy.types.TypeEngine.bind_expression>,
and in this case you'd be CASTing to String so that the SQL
renders as "WHERE id_string = CAST(12345, VARCHAR)".
Thanks!
--
You received this message because you are subscribed to the
Google Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it,
send an email to [email protected] <javascript:>.
To post to this group, send email to [email protected]
<javascript:>.
Visit this group at http://groups.google.com/group/sqlalchemy
<http://groups.google.com/group/sqlalchemy>.
For more options, visit https://groups.google.com/d/optout
<https://groups.google.com/d/optout>.
--
You received this message because you are subscribed to the Google
Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to [email protected]
<mailto:[email protected]>.
To post to this group, send email to [email protected]
<mailto:[email protected]>.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.