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
test2_tb.err
Description: Binary data