Michael,

This is some really great progress. I see you added a new hook into the Mapper called populate_instance. I followed your polymorph2 example to extend my own test2 example. Unfortunately there is an issue with the way I wrote populate_instance. I found that the row passed into my extension Mapper's populate_instance had keys like 'tests_type' and 'tests_id'. After the proper class of the incoming row was identified I called the proper populate_inventory passing in the same arguments (for instance Test2.populate_inventory). Unfortunately, that function was looking for keys like 'type' and 'id' without the 'tests_' prefix, but it was getting the same row with the 'tests_' prefixes. When I threw a quick hack in mapper.py to have the new key be table.name + '_' + c.key, there were some more complicated issues that are over my head at this point.

I was also wondering about two things in this area of the code:
1) What is the translate boolean that populate_inventory takes supposed to signify? I tried giving it both False and True and I just ended up with slightly different tracebacks.
2) the populate_inventory function on the mapper extension from the polymorph2 example returned False or True. What does this mean?


So I've attached my latest test2.py and test2_tb.err files. I imagine that the issue here isn't that big a deal. Thanks for helping me with this. I basically have what I want except that without the populate_inventory function working I still get the extra Null columns when I run a select on the base class.

One last note -- On the one hand, splitting the tables up and joining them in order to do inheritance looks good in the database model. On the other, the code to achieve polymorphic selects (polymorph2 example) gets complicated. I imagine this would especially be the case with a deep inheritance tree. Also, the join slows down the process a bit. Though the method I'm using in the tests2 example has a messy database schema, the code is much easier to follow and the process should be (hopefully) faster than using a join. Let me know what you think.

-Michael
import sys
from sqlalchemy import *
from traceback import print_tb

tests = Table('tests',
    Column('id', Integer, primary_key=True),
    Column('type', String),
    Column('data1', Integer),
    Column('data2', String))


class Test(object):
    _type = 'abstract'
    def __init__(self, *args, **kwargs):
        self.type = self._type
        for key, value in kwargs.items():
            setattr(self, key, value)        
    
class Test1(Test):
    _type = 'test1'
    def __repr__(self): return "Test1: %s" % self.data1

class Test2(Test):
    _type = 'test2'
    def __repr__(self): return "Test2: %s" % self.data2

    
class TestLoader( MapperExtension ):
    def create_instance(self, mapper, row, imap, class_):
        if row['tests_type'] == 'test1': return Test1()
        if row['tests_type'] == 'test2': return Test2()
        raise "InvalidType", "'%s' is an invalid type. " % row['tests_type']

    def populate_instance(self, mapper, instance, row, identitykey, imap, isnew):
        if row['tests_type'] =='test1':
            Test1.mapper.populate_instance(instance, row, identitykey, imap, isnew)
            return False
        if row['tests_type'] =='test2':
            Test2.mapper.populate_instance(instance, row, identitykey, imap, isnew)
            return False
        
        return True    
        
testext = TestLoader()        
test1select = select([tests.c.id, tests.c.type, tests.c.data1]).alias('test1select')
test2select = select([tests.c.id, tests.c.type, tests.c.data2]).alias('test2select')

Test.mapper = mapper(Test, tests, extension=testext)
Test1.mapper = mapper(Test1, test1select)
Test2.mapper = mapper(Test2, test2select)


global_connect('sqlite://', echo=True, echo_uow=True)    

tests.create()

first = Test1(data1=5)
second = Test2(data2="hi")

try:
    objectstore.commit()
    all = Test.mapper.select()
    for test_instance in all:
        print test_instance
except:
    tag, error, traceback = sys.exc_info()
    file = open('test2_tb.err', 'w')
    print_tb(traceback, limit=None, file=file)
    file.close()
    raise
    
    






Attachment: test2_tb.err
Description: Binary data

Reply via email to