hey michael -

I committed another small thing to mapper to help some more with this.  with a regular mapper->mapper inheritance, the submapper can specify the same table as the parent.  I worked up many-to-many relationships on both Test and Test1 (to test that you can stick them on the child objects too) and got a lot going.

the get() method needs some help.  the example includes a customized get() that does the job, so this is another candidate for a MapperExtension method, i.e. override the "get()".

The *next* step when we get this really going, will be a stock MapperExtension that does all this work for you.  then this will be a totally supported feature thats pluggable.

import sys
from sqlalchemy import *
import sqlalchemy.mapping

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

foo = Table('foo', 
    Column('id', Integer, primary_key=True),
    Column('data', String(20)))
    
test_foo = Table('test_foo',
    Column('foo_id', Integer, ForeignKey(foo.c.id)),
    Column('test_id', Integer, ForeignKey(tests.c.id)))

bar = Table('bar', 
    Column('id', Integer, primary_key=True),
    Column('data', String(20)))
    
test_bar = Table('test_bar', 
    Column('bar_id', Integer, ForeignKey(bar.c.id)),
    Column('test_id', Integer, ForeignKey(tests.c.id)))
    
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 %s %s" % (self.data1, repr([f for f in self.foos]), repr([b for b in self.bars]))

class Test2(Test):
    _type = 'test2'
    def __repr__(self): 
        return "Test2: %s %s" % (self.data2, repr([f for f in self.foos]))

class Foo(object):
    pass

class Bar(object):
    pass
            
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):
        print [c for c in row.keys()]
        if row['tests_type'] =='test1':
            Test1.mapper.populate_instance(instance, row, identitykey, imap, isnew, frommapper=mapper)
            return False
        if row['tests_type'] =='test2':
            Test2.mapper.populate_instance(instance, row, identitykey, imap, isnew, frommapper=mapper)
            return False
        
        return True    
        
testext = TestLoader()        

assign_mapper(Foo, foo)
assign_mapper(Bar, bar)
Test.mapper = mapper(Test, tests, extension=testext, properties={
    'foos' : relation(Foo, secondary=test_foo)
})
Test1.mapper = mapper(Test1, tests, inherits=Test.mapper, properties = {
    'bars': relation(Bar, secondary=test_bar)
})
Test2.mapper = mapper(Test2, tests, inherits=Test.mapper)

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

tests.create()
foo.create()
bar.create()
test_foo.create()
test_bar.create()

first = Test1(data1=5)
second = Test2(data2="hi")
first.foos.append(Foo(data='foo1'))
first.bars.append(Bar(data='bar1'))
second.foos.append(Foo(data='foo2'))

print "----------------\nCOMMIT\n"
objectstore.commit()

def get_test(id):
    t1key = Test1.mapper.identity_key(id)
    t2key = Test2.mapper.identity_key(id)
    if objectstore.has_key(t1key):
        return Test1.mapper.get(id)
    elif objectstore.has_key(t2key):
        return Test2.mapper.get(id)
    else:
        return Test.mapper.get(id)    
        
print "--------------\nGET\n"
print get_test(1)
objectstore.clear()
print "--------------\nSELECTALL\n"
all = Test.mapper.select()
for test_instance in all:
    print test_instance








On Mar 26, 2006, at 8:09 PM, Michael Carter wrote:

It seems that this problem is giving me more trouble than I originally thought. If I create a many-to-many relation between the baseclass Test and some other class Foo to get the attribute foos, then it doesn't work with Test1 and Test2 instances. a = Test1(); a.foos.append(Foo()); objectstore.commit() => only adds the new Foo() but doesn't make an entry into the tests_foos table.

I think this is a related problem in that some of these properties are storing Test1 or Test2 as class_ when they need to be storing Test. Let me know what you think the problem is.

-Michael

On 3/26/06, Michael Carter <[EMAIL PROTECTED]> wrote:
Great,
I'll check out the latest revision. There was another issue that I ran into a couple hours ago. That is, if you turn database echo on for the test2 example you can see that every time you make a call like Test.get(1) or Test.get(2) the object is reloaded from the database. I believe that this is because identity_key is called on the Test classes mapper so you get something like (<class 'Test'>, (2,)) as the identity_key, but then when a Test2 or Test1 does a lookup it uses a  key like (<class 'Test2'>, (2,)). A very quick hack I did was to change mapper.py so the identity_key function looked like this:

        if hasattr(self.class_, '_base'):
            return objectstore.get_id_key(tuple(primary_key), self.class_._base)
        return objectstore.get_id_key(tuple(primary_key), self.class_)

Then I added _base = Test to both the Test1 and Test2 class. I know this is a very fragile and far less than ideal fix -- I was just trying to isolate the problem. I'm not sure where that information could or should actually be stored.

Thanks again for extending the code to make these examples possible.

-Michael


Reply via email to