Ok, theres a lot going on with this one....

the biggest problem youre hitting is that Element.mapper is made against the table "element_table", so when adding a property to Element.mapper, the primaryjoin has to somehow get back to that table. Element's mapper doesnt know anything about the "element_type_join" selectable so it cannot create synchronization rules for that relation. Similarly, if you try putting a backref directly into the SpecLine mapper's 'master' property, which is in theory how it should be done, that fails also since you are dealing with the non-primary mapper for the Element table....this has to be that way since only one Mapper is responsible for the actual saving of an object, that is the primary mapper.

So to work around that, you can set up a two-way "specification" property on the Element class by adding another property to SpecLine, which I called "_element", and it just deals with the Element class directly. While this allows the example to save to the DB (with some other adjustments i will describe), Im not sure if this workaround is going to really hold up. the general problem is that the polymorph thing I came up with, which is not really a fully built- in feature as of yet (but obviously should be), doesnt have great support for backrefs.

the next problem is, "add_property" and also "backref" which relies upon add_property, doesnt work so well with an inheritance chain...because when you say m = mapper(bar, table, inherits=foo), it copies all the properties from 'foo' to 'bar'. when you do add_property to 'foo', that doesnt do anything for 'bar'. i will check this in as a trac ticket. so to work around that I set up the SpecLine mapper with the _element property much earlier, and add the "polymorphic" properties later on via add_property.

*then*, in your example you are appending a Detail object to the "specification" list....I assume you never got it going that far, but "specification" is a list of SpecLine objects, so I switched that.

anyway, you do all that and you have whats attached. It commits, but since you are touching upon at least two patterns that arent supported/fully working, not sure how far it will fly as of yet.

- mike


from sqlalchemy import *


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


element_table = Table('elements',
    Column('element_id', Integer, primary_key=True),
    Column('name', String(128))).create()

detail_table = Table('details',
    Column('element_id', Integer, ForeignKey('elements.element_id'),
        primary_key=True),
    Column('material', String(128), default=''),
    Column('weight', Float, default=0)).create()

assembly_table = Table('assemblies',
    Column('element_id', Integer, ForeignKey('elements.element_id'),
        primary_key=True)).create()

specification_table = Table('specification',
    Column('spec_line_id', Integer, primary_key=True),
    Column('master_id', Integer, ForeignKey("elements.element_id"),
        nullable=False),
    Column('slave_id', Integer, ForeignKey("elements.element_id"),
        nullable=True),
    Column('quantity', Float, default=1)).create()

class Element(object):
    def __repr__(self):
        return '<%s %s>' % (self.__class__.__name__, self.name)

class Detail(Element): pass

class Assembly(Element): pass

class SpecLine(object):
    def __repr__(self):
        return '<%s %.01f %s>' % (self.__class__.__name__,
            self.quantity, getattr(self.slave, 'name', None))

assign_mapper(Element, element_table,
    properties=dict(name=element_table.c.name))


assign_mapper(SpecLine, specification_table, properties=dict(
        _master = relation(Element.mapper, backref="specification"),
        quantity=specification_table.c.quantity
        )
        )

assign_mapper(Assembly, assembly_table, inherits=Element.mapper)

assign_mapper(Detail, detail_table, inherits=Element.mapper)


detail_join = select([element_table,
    detail_table.c.material,
    detail_table.c.weight,
    column("'detail'").label('type')],
    element_table.c.element_id==detail_table.c.element_id)

assembly_join = select([element_table,
    null().label('material'),
    null().label('weight'),
    column("'assembly'").label('type')],
    element_table.c.element_id==assembly_table.c.element_id)

element_type_join = detail_join.union_all(assembly_join).alias('pjoin')


class Extension(MapperExtension):
    def create_instance(ext, mapper, row, imap, class_):
        if row['pjoin_type'] == 'detail':
            return Detail()
        elif row['pjoin_type'] == 'assembly':
            return Assembly()
        else:
            return Element()

element_type_mapper = mapper(Element, element_type_join, extension=Extension())


SpecLine.mapper.add_property('master', relation(element_type_mapper,
            primaryjoin=specification_table.c.master_id==element_type_mapper.c.element_id,
            foreignkey=specification_table.c.master_id,
            lazy=True, uselist=False,
            )
            )
        
SpecLine.mapper.add_property('slave', relation(element_type_mapper,
            primaryjoin=specification_table.c.slave_id==element_type_mapper.c.element_id,
            foreignkey=specification_table.c.slave_id,
            lazy=False, uselist=False),
        )

a1 = Assembly(name='a1')
a1.specification.append(SpecLine())
objectstore.commit()




On Apr 10, 2006, at 8:04 AM, Vladimir Iliev wrote:

Hi, can someone tell me, what's wrong with the attached SA mapping ?
<test.py>

Reply via email to