On Fri, Nov 11, 2016 at 12:20 PM, Michael Williamson <mich...@healx.io> wrote:
> I'm using hybrid_property, and would like the key of the property to be set
> to the attribute name, rather than the name of the getter. This is because
> I'm generating a getter function based on some args, rather than having the
> caller directly defining the getter. As a minimal example:
>
> from __future__ import unicode_literals
>
> from sqlalchemy import create_engine, Column, Integer, String
> from sqlalchemy.ext.hybrid import hybrid_property
> from sqlalchemy.ext.declarative import declarative_base
> from sqlalchemy.orm import Session
>
>
> def prefixed_property(other_column, prefix):
>     def instance_get(self):
>         return prefix + getattr(self, other_column.key)
>
>     return hybrid_property(fget=instance_get)
>
>
> Base = declarative_base()
>
> class Person(Base):
>     __tablename__ = "person"
>
>     id = Column(Integer, primary_key=True)
>     name = Column(String)
>     greeting = prefixed_property(name, "Hello ")
>
>
> engine = create_engine("sqlite:///:memory:")
>
> Base.metadata.create_all(engine)
> session = Session(engine)
>
> session.add(Person(name="Bob"))
> session.commit()
>
> assert session.query(Person.greeting).one() == ("Hello Bob", )
> assert session.query(Person.greeting).one().greeting == "Hello Bob" # Fails
> with AttributeError since hybrid_property uses instance_get as the name
>
> Is there a sensible way to do this? Or am I going about the problem the
> wrong way? In practice, I'm using this to define properties backed by a JSON
> field. In case it makes any different, here's the actual use-case I have:
>
> from __future__ import unicode_literals
>
> import os
>
> from sqlalchemy import create_engine, Column, Integer
> from sqlalchemy.dialects.postgresql import JSONB
> from sqlalchemy.event import listens_for
> from sqlalchemy.ext.hybrid import hybrid_property
> from sqlalchemy.ext.declarative import declarative_base
> from sqlalchemy.orm import Session
> from sqlalchemy.pool import StaticPool
>
>
> def json_property(json_column, name, type_=None):
>     def instance_get(self):
>         return getattr(self, json_column.key)[name]
>
>     def instance_set(self, value):
>         json_obj = getattr(self, json_column.key)
>         if json_obj is None:
>             setattr(self, json_column.key, {})
>         getattr(self, json_column.key)[name] = value
>
>     def cls_get(cls):
>         expression = json_column[name]
>         if type_:
>             return expression.astext.cast(type_)
>         else:
>             return expression
>
>     return hybrid_property(fget=instance_get, expr=cls_get,
> fset=instance_set)
>
>
> Base = declarative_base()
>
> class Person(Base):
>     __tablename__ = "person"
>
>     id = Column(Integer, primary_key=True)
>     data = Column(JSONB, nullable=False)
>     born = json_property(data, "born", Integer())
>
>
> engine = create_engine(os.environ["TEST_DATABASE"], poolclass=StaticPool)
> engine.execute("SET search_path TO pg_temp")
>
> @listens_for(engine, "engine_connect")
> def set_search_path(connection, branch):
>     connection.execute("SET search_path TO pg_temp")
>
>
> Base.metadata.create_all(engine)
> session = Session(engine)
>
> session.add(Person(born=1881))
> session.commit()
>
> assert session.query(Person.born).one() == (1881, ) # Works fine
> assert session.query(Person.born).one().born == 1881 # Raises AttributeError
> since hybrid_property uses instance_get as the name
>

I think your code is basically fine, you've just got a mistake on the
last line. Presumably you meant to query Person, not Person.born?

assert session.query(Person).one().born == 1881

hybrid_property does't care about the name of the "fget" function, it
just calls it, passing the instance as the only parameter:

https://bitbucket.org/zzzeek/sqlalchemy/src/f2eb4aac9517a3775411c2ecf0f588ffd0d790f6/lib/sqlalchemy/ext/hybrid.py?at=master&fileviewer=file-view-default#hybrid.py-744

Hope that helps,

Simon

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
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 sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to