Help! More Class->Mapper confusion!

13 views
Skip to first unread message

batraone

unread,
Mar 11, 2009, 5:09:54 PM3/11/09
to sqlalchemy
Hi,

OK - I ran into another thing that I do not understand about
sqlalchemy. I am a newbie, so hopefully this will be
straightforward.

There are two issues here:

ISSUE1:
I create a class called foo w/ a method called name().
I map a table that has a column called 'name' but use
the column_prefix = '_' but I do not see _name get added
to the class! But other labels, such as _phone which do
not have a corresponding method name does get added.

Here's the code:

import sqlalchemy as sa
from sqlalchemy import orm
from sqlite3 import dbapi2 as sqlite

class Foo(object):
def __init__(self):
self._myname = 'Bar'

def name(self):
return(self._myname)

class FooStore:
def __init__(self):
self.metadata = sa.MetaData()

# table to map class to
self.t_foo = sa.Table('table_foo', self.metadata,
sa.Column('id', sa.types.Integer, primary_key=True),
sa.Column('name', sa.types.String(100)),
sa.Column('phone', sa.types.String(100))
)

def map(self, myfoo):
'Creates the map. '

orm.mapper(Foo, self.t_foo, column_prefix = '_')

# Add foo to the database
print dir(myfoo) # where did _name go?, _phone is there!

mf2 = Foo() # ok, let's create a new one.
print dir(mf2) # same problem

if __name__ == '__main__':
f = Foo()
fs = FooStore()
fs.map(f)


ISSUE2: I have an object that will be given to me that I want to
store into a database. The class definition is located in a package.
When I map this class to a table and set the attribute - I get
an exception:

AttributeError: 'NoneType' object has no attribute 'set'

This can be seen by modifying the above example - where I put
Foo into a package called 'foo':

import sqlalchemy as sa
from sqlalchemy import orm
from sqlite3 import dbapi2 as sqlite

import sys

import foo.Foo as Foo

class FooStore:
def __init__(self):
self.metadata = sa.MetaData()

# table to map class to
self.t_foo = sa.Table('table_foo', self.metadata,
sa.Column('id', sa.types.Integer, primary_key=True),
sa.Column('name', sa.types.String(100)),
sa.Column('phone', sa.types.String(100))
)

def map(self, myfoo):
'Creates the map. te is the test engine'

orm.mapper(Foo.Foo, self.t_foo, column_prefix = '_')

# Add foo to the database

try:
myfoo._phone = '555-1212' # exception is thrown!

except:
#AttributeError: 'NoneType' object has no attribute 'set'
print sys.exc_info()

mf2 = Foo.Foo() # ok, let's create a new one.
# AttributeError: 'Foo' object has no attribute
'_sa_instance_state'
myfoo._phone = '555-1212' #

if __name__ == '__main__':
orm.clear_mappers()
f = Foo.Foo()
fs = FooStore()
fs.map(f)



What's the right way to adapt this class to a table?

Thanks!

-Raj

Michael Bayer

unread,
Mar 11, 2009, 5:48:51 PM3/11/09
to sqlal...@googlegroups.com
batraone wrote:
> def map(self, myfoo):
> 'Creates the map. '
>
> orm.mapper(Foo, self.t_foo, column_prefix = '_')
>
> # Add foo to the database
> print dir(myfoo) # where did _name go?, _phone is there!
>
> mf2 = Foo() # ok, let's create a new one.
> print dir(mf2) # same problem
>
> if __name__ == '__main__':
> f = Foo()
> fs = FooStore()
> fs.map(f)

The column_prefix wasn't being honored when the mapper checked for
existing names, this is fixed in trunk r5839.

But also, don't create instances of the object before the class is mapped.
In particular, it's bad form to create tables and mappers inside of class
methods. Create class-level constructs like tables and mappers at the
module level, in the same scope in which you create your classes.


>
> mf2 = Foo.Foo() # ok, let's create a new one.
> # AttributeError: 'Foo' object has no attribute
> '_sa_instance_state'
> myfoo._phone = '555-1212' #
>
> if __name__ == '__main__':
> orm.clear_mappers()
> f = Foo.Foo()
> fs = FooStore()
> fs.map(f)

don't create instances of the object before the class is mapped.

batraone

unread,
Mar 11, 2009, 6:05:49 PM3/11/09
to sqlalchemy
Thanks. I don't have control over the instance creation. These pre-
created objects are handed down to me. I could create an adapter that
maps the original class to the one that has been mapped but this is
quite a bit of work as I have to manually copy over each of the source
class attributes to my mapped class. I could be clever and perhaps
look for common attribute names via the internal dictionary but this
too seems clunky.

I would think this is a common problem (augmenting a class to dump
it's contents to a db but keep the original class untouched (sort of
like shelve - but w/o the
restore capability)). Is there a better way to handle this?

Thanks,

Raj
> don't create instances of the object before the class is mapped.- Hide quoted text -
>
> - Show quoted text -

Michael Bayer

unread,
Mar 11, 2009, 8:41:37 PM3/11/09
to sqlal...@googlegroups.com

On Mar 11, 2009, at 6:05 PM, batraone wrote:

>
> Thanks. I don't have control over the instance creation. These pre-
> created objects are handed down to me. I could create an adapter that
> maps the original class to the one that has been mapped but this is
> quite a bit of work as I have to manually copy over each of the source
> class attributes to my mapped class. I could be clever and perhaps
> look for common attribute names via the internal dictionary but this
> too seems clunky.
>
> I would think this is a common problem (augmenting a class to dump
> it's contents to a db but keep the original class untouched (sort of
> like shelve - but w/o the
> restore capability)). Is there a better way to handle this?

Unfortunately the usage of an object relational mapper assumes that
some degree of convention can be applied to the classes being mapped.
For example, you can't map "old style" classes nor can you map most
natively-backed classes (like cElementTree structures),
instrumentation will fail if the class relies upon direct __dict__
access, and there's probably many other examples of limitations
here. Applying instrumentation to a class to which you don't have
control over is generally a bad idea no matter what persistence
approach you're using, as even if you do get it working, changes to
the implementation of those objects will break your application. So
in this case you definitely should be marshalling these external
objects immediately into an object structure which you have control
over. You also should most certainly be using standard Python
idioms like dir(object), getattr() and possibly __dict__ access in
order to marshall the state of the incoming objects into your own
model using a reflective approach rather than hardcoding every
attribute.


Reply all
Reply to author
Forward
0 new messages