On Aug 11, 2010, at 10:51 AM, Ross Vandegrift wrote:

> Hey everyone,
> 
> I *think* this is a limitation of having classes that are
> polymorphic_on a join (instead of mapping a join), but I'm going to
> ask anyway to make sure I understand.
> 
> I have a hierarchy of mappers to represent various network interfaces
> on various devices.  This is polymorphic_on a join to the
> discriminator of the assocated device records:
> 
> -----
> iftype_join = select([device_t.c.model], 
> from_obj=[interface_t.join(device_t)]).alias()
> interface_m = orm.mapper(InterfaceBase, interface_t,
>                         polymorphic_on=iftype_join.c.model,
>                         polymorphic_identity=0,
>                         properties={"device": orm.relation(Device),
>                                     "binding": orm.relation(Binding)})
> switchport_m = orm.mapper(SwitchPort, inherits=interface_m, 
> polymorphic_identity=1)
> ciscoswitchport_m = orm.mapper(CiscoSwitchPort, inherits=switchport_m, 
> polymorphic_identity=2)
> juniperswitchport_m = orm.mapper(JuniperSwitchPort, inherits=switchport_m, 
> polymorphic_identity=3)
> routerport_m = orm.mapper(RouterPort, inherits=interface_m, 
> polymorphic_identity=4)
> ciscorouterport_m = orm.mapper(CiscoRouterPort, inherits=routerport_m, 
> polymorphic_identity=5)
> juniperrouterport_m = orm.mapper(JuniperRouterPort, inherits=routerport_m, 
> polymorphic_identity=6)


that polymorphic_on above is not valid.  polymorpic_on needs to be a column 
that's in the mapped table, in this case interface_t, or in the 
with_polymorphic selectable if any.  Its a SQLA bug that this condition is 
silently accepted - a warning is emitted as of rf9c64cf0cbb9.    We'll push 
this to an exception in the next major version.



> -----
> 
> 
> Next, a binding ties together groups of router and switch ports:
> 
> -----
> binding_m = orm.mapper(Binding, binding_t,
>                       properties={'routerports': orm.relation(RouterPort),
>                                   'switchports': orm.relation(SwitchPort)})
> -----
> 
> But, when I access these members, the collections aren't restricted to
> the appropriate subclasses RouterPort/SwitchPort.  They include all
> interface types:
> 
>>>> print [type(x) for x in b1.routerports]
> [<class '__main__.CiscoRouterPort'>, <class '__main__.CiscoSwitchPort'>]
> 
> Here's the generated SQL:
> 
> 2010-08-11 10:29:42,560 INFO sqlalchemy.engine.base.Engine.0x...60ec BEGIN
> 2010-08-11 10:29:42,561 INFO sqlalchemy.engine.base.Engine.0x...60ec SELECT 
> binding.id AS binding_id 
> FROM binding 
> WHERE binding.id = ?
> 2010-08-11 10:29:42,561 INFO sqlalchemy.engine.base.Engine.0x...60ec [1]
> 2010-08-11 10:29:42,561 INFO sqlalchemy.engine.base.Engine.0x...60ec SELECT 
> interface.id AS interface_id, interface.deviceid AS interface_deviceid, 
> interface.bindingid AS interface_bindingid 
> FROM interface 
> WHERE ? = interface.bindingid
> 2010-08-11 10:29:42,561 INFO sqlalchemy.engine.base.Engine.0x...60ec [1]
> 
> Is this a limitation of being polymorphic_on a join instead of mapping
> the join?
> 
> Thanks,
> Ross
> 
> 
> from sqlalchemy import *
> import sqlalchemy.orm as orm
> 
> class Device(object):
>    pass
> class Switch(Device):
>    pass
> class CiscoSwitch(Switch):
>    pass
> class JuniperSwitch(Switch):
>    pass
> class Router(Device):
>    pass
> class CiscoRouter(Router):
>    pass
> class JuniperRouter(Router):
>    pass
> 
> class InterfaceBase(object):
>    pass
> class SwitchPort(InterfaceBase):
>    pass
> class CiscoSwitchPort(SwitchPort):
>    pass
> class JuniperSwitchPort(SwitchPort):
>    pass
> class RouterPort(InterfaceBase):
>    pass
> class CiscoRouterPort(RouterPort):
>    pass
> class JuniperRouterPort(RouterPort):
>    pass
> 
> class Binding(object):
>    pass
> 
> engine = create_engine('sqlite:///:memory:')
> metadata = MetaData()
> device_t = Table('device', metadata, 
>                 Column('id', Integer, primary_key=True),
>                 Column('model', Integer))
> interface_t = Table('interface', metadata,
>                    Column('id', Integer, primary_key=True),
>                    Column('deviceid', Integer,
> ForeignKey('device.id')),
>                    Column('bindingid', Integer,
> ForeignKey('binding.id')))
> binding_t = Table('binding', metadata,
>                  Column('id', Integer, primary_key=True))
> 
> 
> metadata.create_all(engine)
> 
> device_m = orm.mapper(Device, device_t,
>                      polymorphic_on=device_t.c.model,
>                      polymorphic_identity=0)
> switch_m = orm.mapper(Switch, inherits=device_m,
>                      polymorphic_identity=1)
> ciscoswitch_m = orm.mapper(CiscoSwitch, inherits=switch_m,
>                           polymorphic_identity=2)
> juniperswitch_m = orm.mapper(JuniperSwitch, inherits=switch_m,
>                             polymorphic_identity=3)
> router_m = orm.mapper(Router, inherits=device_m,
>                      polymorphic_identity=4)
> ciscorouter_m = orm.mapper(CiscoRouter, inherits=router_m,
>                           polymorphic_identity=5)
> juniperrouter_m = orm.mapper(JuniperRouter, inherits=router_m,
>                             polymorphic_identity=6)
> 
> iftype_join = select([device_t.c.model],
> from_obj=[interface_t.join(device_t)]).alias()
> interface_m = orm.mapper(InterfaceBase, interface_t,
>                         polymorphic_on=iftype_join.c.model,
>                         polymorphic_identity=0,
>                         properties={"device": orm.relation(Device),
>                                     "binding":
> orm.relation(Binding)})
> switchport_m = orm.mapper(SwitchPort, inherits=interface_m,
>                          polymorphic_identity=1)
> ciscoswitchport_m = orm.mapper(CiscoSwitchPort, inherits=switchport_m,
>                               polymorphic_identity=2)
> juniperswitchport_m = orm.mapper(JuniperSwitchPort,
> inherits=switchport_m,
>                                 polymorphic_identity=3)
> routerport_m = orm.mapper(RouterPort, inherits=interface_m,
>                          polymorphic_identity=4)
> ciscorouterport_m = orm.mapper(CiscoRouterPort, inherits=routerport_m,
>                               polymorphic_identity=5)
> juniperrouterport_m = orm.mapper(JuniperRouterPort,
> inherits=routerport_m,
>                                 polymorphic_identity=6)
> 
> binding_m = orm.mapper(Binding, binding_t,
>                       properties={'routerports':
> orm.relation(RouterPort),
>                                   'switchports':
> orm.relation(SwitchPort)})
> 
> Session = orm.sessionmaker(bind=engine)
> session = Session()
> 
> b1 = Binding()
> b2 = Binding()
> 
> r1 = CiscoRouter()
> rp1 = CiscoRouterPort()
> rp1.device = r1
> rp1.binding = b1
> 
> r2 = JuniperRouter()
> rp2 = JuniperRouterPort()
> rp2.device = r2
> rp2.binding = b2
> 
> s1 = CiscoSwitch()
> sp1 = CiscoSwitchPort()
> sp1.device = s1
> sp1.binding = b1
> 
> s2 = JuniperSwitch()
> sp2 = JuniperSwitchPort()
> sp2.device = s2
> sp2.binding = b2
> 
> session.add_all([r1, rp1, r2, rp2, s1, sp1, s2, sp2, b1, b2])
> session.flush()
> session.commit()
> 
> 
> engine.echo = True
> print [type(x) for x in b1.routerports]
> print [type(x) for x in b1.switchports]
> print [type(x) for x in b2.routerports]
> print [type(x) for x in b2.switchports]
> 
> 
> -- 
> Ross Vandegrift
> r...@kallisti.us
> 
> "If the fight gets hot, the songs get hotter.  If the going gets tough,
> the songs get tougher."
>       --Woody Guthrie

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalch...@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to