On 11/11/2016 07:20 AM, Michael Williamson 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
this example fails because the object returned is not a Person instance,
it is the raw value of the column returned by your hybrid which in this
case is anonymously named, as the SQL expression is "x + y". You need
to give it the label of your choice for it to be present in the row in a
particular way. There is some logic these days inside of expression()
that seems to get in the way, so just subclass or use a plain descriptor:
class prefixed_property(hybrid_property):
def __init__(self, other_column, prefix, labelname):
self.other_column = other_column
self.prefix = prefix
self.labelname = labelname
def __get__(self, instance, owner):
if instance is None:
retval = self.prefix + getattr(owner, self.other_column.key)
return retval.label(self.labelname)
else:
return self.prefix + getattr(instance, self.other_column.key)
class Person(Base):
__tablename__ = "person"
id = Column(Integer, primary_key=True)
name = Column(String)
greeting = prefixed_property(name, "Hello ", "greeting")
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
--
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
<mailto:sqlalchemy+unsubscr...@googlegroups.com>.
To post to this group, send email to sqlalchemy@googlegroups.com
<mailto:sqlalchemy@googlegroups.com>.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.
--
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.