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>