alastair -

the thing i am working on currently in 0.2 is:

        mapper(Person, people)
        mapper(Engineer, engineers, inherits=class_mapper(Person))
        mapper(Manager, managers, inherits=class_mapper(Person))

        people_mapper = mapper(
                        Person, person_join,
                        polymorphic_on=person_join.c.type,
                        polymorphic_map={'engineer':Engineer, 
'manager':Manager},
                        non_primary=True
        )

so no MapperExtension would be needed anymore. itll have to keep track of using the proper mappers to generate identity_keys and all that. perhaps, after the above is working, the syntax could get even more sparse and not require the explicit three mappers ahead of time, but im not sure about that yet.

The same arguments would also be just as useful with a single Mapper that is doing single-table inheritance.

I think the original polymorph blog post and subsequent discussion, while it wasnt an extremely useful thing by itself, was the natural evolution of adding a full-blown feature to the Mapper.


On Apr 27, 2006, at 10:41 AM, Alastair Houghton wrote:

Hi all,

As is probably obvious from some of my previous posts, I'm trying to use a polymorphic mapper (via a MapperExtension). As is probably also obvious, there seem to be a few problems. Anyway, I think I've got to the bottom of one of them; in Michael's example "polymorph2.py", the MapperExtension looks like this:

    # MapperExtension object.
    class PersonLoader(MapperExtension):
        def create_instance(self, mapper, row, imap, class_):
            if row[person_join.c.type] =='engineer':
                return Engineer()
            elif row[person_join.c.type] =='manager':
                return Manager()
            else:
                return Person()

def populate_instance(self, mapper, session, instance, row, identitykey, imap, isnew):
            if row[person_join.c.type] =='engineer':
Engineer.mapper.populate_instance(session, instance, row, identitykey, imap, isnew, frommapper=mapper)
                return False
            elif row[person_join.c.type] =='manager':
Manager.mapper.populate_instance(session, instance, row, identitykey, imap, isnew, frommapper=mapper)
                return False
            else:
                return sqlalchemy.mapping.EXT_PASS

which is fine *except* that if you look carefully, you'll spot that you can easily end up with more than one instance of e.g. Engineer or Manager for the same person_id, because the _instance() method in mapper.py initially generates the identity_key (which is the key SQLAlchemy uses to ensure that object instances are unique) from the Person mapper, not from the Engineer or Manager mappers. As a result, it won't find a pre-existing Engineer or Manager object, and all sorts of crazy behaviour can then ensue.

There are a few possible approaches to fixing this:

1. Add another method to the MapperExtension class to find the mapper to use for identity key generation. This has the advantage that it's straightforward, and it also maintains the existing MapperExtension-based approach. However, the disadvantage is that the MapperExtension code required for polymorphism is getting less and less obvious.

2. Add a method to MapperExtension specifically to support polymorphism, and push the (effects of) the code above down into Mapper. This has the advantage that the implementation of polymorphism won't require mapping the row data into the required form for the target class's mapper twice (like it does at the moment), and it also would simplify using a MapperExtension for this purpose. But perhaps this is too specialised a change to a generic extension mechanism?

3. Add polymorphism support directly to the Mapper class itself. So instead of writing

   mapper(Person, person_join, extension=personLoaderExtension)

you might write

   mapper(Person, person_join, type_selector=person_join.c.type,
          type_map={ 'engineer': Engineer, 'manager': Manager })

This would be easier to understand IMO, and doesn't preclude use of a MapperExtension for any other purpose.

I want to use this functionality, so I'm happy to do the work and submit a patch; I'm just trying to work out what everyone prefers syntax/design-wise.

Kind regards,

Alastair.

--
http://www.alastairs-place.net




-------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security? Get stuff done quickly with pre-integrated technology to make your job easier Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo http://sel.as-us.falkag.net/sel? cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Sqlalchemy-users mailing list
Sqlalchemy-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sqlalchemy-users



-------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Sqlalchemy-users mailing list
Sqlalchemy-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sqlalchemy-users

Reply via email to