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.