Get "properties" of a class mapped with SqlAlchemy (and its "sqlalchemy.orm.synonym")

1,426 views
Skip to first unread message

Hector Blanco

unread,
Dec 20, 2010, 7:30:28 PM12/20/10
to sqlal...@googlegroups.com
Hello all!

I have an application running under Python2.6 and the classes are set
up with properties (in a Python2.4 style, though).

Everything seems to be working fine with SqlAlchemy (version 0.6.5,
just in case) as it explains here:
http://www.sqlalchemy.org/docs/orm/extensions/declarative.html#defining-synonyms

The problem is that sometimes I want to get the properties of a class
without knowing in advance the name or number of said properties.

Before introducing SqlAlchemy, I had a little function that extracted
them from the class (the __class__ attribute in an instance):

def iter_properties_of_class(cls):
retval = list()
for varname in vars(cls):
value = getattr(cls, varname)
if isinstance(value, property):
list.append(varname)
return retval

Now they're not instance of property anymore.

I dug out a little and I found that what before were “properties” now
are type: <class 'sqlalchemy.orm.attributes.propertyProxy'> So I
thought... oh, ok... then I just have to check if they're instance of
that propertyProxy class... And so I changed my “auxiliary” method to:
import sqlalchemy
[ . . . ]
if isinstance(getattr(cls, varname), sqlalchemy.orm.attributes.propertyProxy):
retval.append(varName)
but I get this error:
'module' object has no attribute 'propertyProxy'

I also tried with...
if isinstance(getattr(cls, varname),
sqlalchemy.orm.attributes.propertyProxy.propertyProxy)
… getting the same error,

or to import propertyProxy directly...
from sqlalchemy.orm.attributes import propertyProxy
… getting:
ImportError: cannot import name propertyProxy


So here's the question:
Is there any way to get the properties of a class mapped with SqlAlchemy?

Just in case, all my classes (at least for the moment) are implemented
using the Declarative method. An small example could be my "User" class:

------------ User.py ---------

class User(Base):
"""Represents a user"""
__tablename__ = "users"

_id = Column("id", Integer, primary_key=True)
_userName = Column("user_name", String(50))
_password = Column("password", String(64))

def __init__(self):
"""Initialize object"""
self._id = -1
self._userName = ""
self._password = ""

def setId(self, id):
"""Set id"""
self._id = int(id)

def getId(self):
"""Get id"""
return self._id
def setUserName(self, userName):
"""Set userName"""
self._userName = userName

def getUserName(self):
"""Get userName"""
return self._userName

def setPassword(self, password):
"""Set password"""
m = hashlib.sha256()
m.update(password)
self._password = m.hexdigest()

def getPassword(self):
"""Get password"""
return self._password

id = synonym('_id', descriptor=property(getId, setId))
userName = synonym('_userName', descriptor=property(getUserName, setUserName))
password = synonym('_password', descriptor=property(getPassword, setPassword))
---------------------

I'd like to know the best way to get a list() containing: ["id",
"userName", "password"]

Thank you in advance!

Michael Bayer

unread,
Dec 20, 2010, 7:51:57 PM12/20/10
to sqlal...@googlegroups.com

I'd look at the object to see if it has a __get__() method, since that's what defines a "descriptor" in Python, not just isinstance(x, property). duck typing


Hector Blanco

unread,
Dec 21, 2010, 10:52:47 AM12/21/10
to sqlal...@googlegroups.com
First of all, thank you for replying.

I don't really know if I understood your idea.

I dug a bit more in the User class (not the instance, but what it
would be self.__class__) and the problem is that both "password" and
"_password" have a "__get__":


I changed the getProperties method a bit, to introspect the __class__ thing:

def getProperties(cls):
properties = list()

for varName in vars(cls):
log.debug("Studying prop '%s' of type: %s" %(varName,
type(getattr(cls, varName))))
if varName == "password" or varName == "_password":
valTmp = getattr(cls, varName)
print(" \t Has %s a __get()__? %s" % (varName, getattr(valTmp, "__get__")))
print(" \t Contents of %s" % varName)
for key, val in valTmp.__dict__.iteritems():
print(" \t\t %s: %s" % (key, val))

return None
#return properties

----- And it outputs this (showing only the password thing:) ---------

Studying prop '_password' of type: <class
'sqlalchemy.orm.attributes.InstrumentedAttribute'>
Has _password a __get()__? <bound method
InstrumentedAttribute.__get__ of
<sqlalchemy.orm.attributes.InstrumentedAttribute object at 0xa26e46c>>
Contents of _password
parententity: Mapper|User|users
__doc__: None
impl: <sqlalchemy.orm.attributes.ScalarAttributeImpl object at 0xa3d758c>
key: _password
comparator: <sqlalchemy.orm.properties.Comparator object at 0xa26e44c>
Studying prop 'password' of type: <class
'sqlalchemy.orm.attributes.propertyProxy'>
Has password a __get()__? <bound method propertyProxy.__get__ of
<sqlalchemy.orm.attributes.propertyProxy object at 0xa26eacc>>
Contents of password
_comparator: <function comparator at 0xa269bc4>
key: password
descriptor: <property object at 0xa25ef7c>
_parententity: Mapper|User|users
user_prop: <property object at 0xa25ef7c>
__doc__: Get password
impl: <sqlalchemy.orm.attributes._ProxyImpl object at 0xa26eaec>

------------------

I have also tried to check isinstance(getattr(cls, varName),
sqlalchemy.orm.attributes.InstrumentedAttribute) (even though it may
not be the best option, but...) and the problem is that both
"password" and "_password" happen to be InstrumentedAttributes
(propertyProxy extends from InstrumentedAttribute).

I've seen in the "attributes.py" file an "is_instrumented" method...
Maybe I could get the vars of an instance (not the class, no... an
instance) which would give me:
(["_sa_instance_state", "_id", "_userName", "_password"]),
then check if these variables are instrumented ("_sa_instance_state"
isn't) and then check if the class has the attributes ["id",
"userName" and "password"] but in order to do that I need to remove
the first character of the attribute name (to get "userName" from
"_userName") and that seems it's going to mess up with the
performance...

Thank you!

2010/12/20 Michael Bayer <mik...@zzzcomputing.com>:


>
> On Dec 20, 2010, at 7:30 PM, Hector Blanco wrote:
>
>> Hello all!
>>
>> I have an application running under Python2.6 and the classes are set
>> up with properties (in a Python2.4 style, though).

[ . . . ]


>> So here's the question:
>> Is there any way to get the properties of a class mapped with SqlAlchemy?
>
> I'd look at the object to see if it has a __get__()  method, since that's what defines a "descriptor" in Python, not just isinstance(x, property).   duck typing
>
>

> --
> You received this message because you are subscribed to the Google Groups "sqlalchemy" group.
> To post to this group, send email to sqlal...@googlegroups.com.
> To unsubscribe from this group, send email to sqlalchemy+...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
>
>

Hector Blanco

unread,
Dec 21, 2010, 11:14:04 AM12/21/10
to sqlal...@googlegroups.com
I found a "maybe" way... but I don't know if it's good idea... the
"propertyProxy" instances have a field called "descriptor" which the
"InstrumentedAttribute" don't have... so I can always do this:

--------------------------------------------------
def getProperties4(cls):
properties = list()

for varKey in vars(cls):
varVal = getattr(cls, varKey)
try:
if "descriptor" in vars(varVal):
if isinstance(getattr(varVal, "descriptor"), property):
properties.append(varKey)
except TypeError:
pass
print("Properties found: '%s'" % properties)
return properties
--------------------------------------------------
That works...
Properties found: '['id', password', 'userName']'

...but I don't really know what I'm exactly touching here... (and how
"dangerous"... or correct it may be)

2010/12/21 Hector Blanco <white...@gmail.com>:

Michael Bayer

unread,
Dec 21, 2010, 11:44:39 AM12/21/10
to sqlal...@googlegroups.com
have you tried mapper.iterate_properties ?

Hector Blanco

unread,
Dec 21, 2010, 12:04:01 PM12/21/10
to sqlal...@googlegroups.com
Ah... much better :)

def getProperties2(instance):
properties = list()
mapper = sqlalchemy.orm.object_mapper(instance)
for prop in mapper.iterate_properties:
if isinstance(prop, sqlalchemy.orm.properties.SynonymProperty):
properties.append(prop.key)
print("::getProperties2 > Returning: %s" % properties)
return properties

Gives me (as wanted):

::getProperties2 > Returning: ['id', 'userName', 'password']

Thank you!

2010/12/21 Michael Bayer <mik...@zzzcomputing.com>:

Reply all
Reply to author
Forward
0 new messages