- changed milestone to 0.5.xx
Polymorphic map: Lookup when discriminator is NULL
Hello again,
this is the second of two tickets I'm going to open regarding the polymorphic map, which I consider a very neat feature of SA. I haven't found much documentation on it though, so I'm not sure whether these are features or bugs - I'd guess the latter.
The polymorphic map, as I understand it, is an alternative to the default polymorphic_identity system and f.e. suitable for setups where you would dynamically decide which class to map to or have more complex rules, such as: - if the discriminator is an instance of int or long, map to class A - if the discriminator is an instance of unicode, map to class B
As I played with the polymorphic map I noticed that when the value of the polymorphic_on column is NULL, the polymorphic map had not been looked up and the "default" object (the object defining polymorphic_on) had been mapped to. I guess you can argue about whether NULL not being a value should be looked up in the map at all. I was expecting it and would rather have it that way, as it may not be a value, but it is a valid scenario you might want to discriminate upon.
I have attached a test to demonstrate this.
Best Regards, Thomas Wiebe
Comments (4)
-
repo owner -
repo owner - changed milestone to 0.6.xx
it does break tests right now to make that change since for a blank eagerloaded row we don't check identity until later on. so removing the discriminator check needs to be more intricate than that.
-
repo owner - changed status to wontfix
here we are, "null" identity key using changesets referenced in
#2238:from sqlalchemy import * from sqlalchemy.orm import * from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import event engine = create_engine('sqlite:///:memory:', echo=True) Base = declarative_base() class User(Base): """ Base user """ __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String) fullname = Column(String) password = Column(String) status_id = Column(Integer) __mapper_args__ = {'polymorphic_on': func.coalesce(status_id, "inactive")} @event.listens_for(User, "init", propagate=True) def init(target, args, kwargs): identity = target.__mapper__.polymorphic_identity if identity == "inactive": target.status_id = None else: target.status_id = identity class ActiveUser(User): """ Active user """ __mapper_args__ = {'polymorphic_identity': 1} class InactiveUser(User): """ Inactive user """ __mapper_args__ = {'polymorphic_identity': "inactive"} Base.metadata.create_all(engine) Session = sessionmaker(bind=engine) session = Session() # data already in DB session.execute( u'INSERT INTO `users` (`name`, `fullname`, `password`, `status_id`)' u'VALUES ("hp", "Hans-Peter", "abcdefg", 1)' ) session.execute( u'INSERT INTO `users` (`name`, `fullname`, `password`, `status_id`)' r'VALUES ("fj", "Franz-Josef", "abcdefg", NULL)' ) # test ORM persistence with our init events session.add(ActiveUser()) session.add(InactiveUser()) session.flush() users = session.query(User).order_by(User.id).all() assert \ [for u in users](type(u)) == \ [InactiveUser, ActiveUser, InactiveUser](ActiveUser,)
-
repo owner - changed milestone to 1.x.xx
- Log in to comment
theres explicit code checking for None and discarding for this:
I can try removing the check to see if any tests were relying upon that somehow, but I prefer
#1131as a solution to an unusual use case such as this one.