- attached inheritance.py
Assigning new item to inherited relation causes error
The attached test case demonstrates the problem. Email has a relation with User, and Companymail inherits from Email, as declared in companymapper.
If in the subclass we now assign a different user via the user-email relation, SQLAlchemy complains with the following traceback. This happens only if a user has been previously assigned to an Email instance, and if we perform the assignment via the subclass.
$ python inheritance.py
Acme, Inc. a@foo Paul
Traceback (most recent call last):
File "inheritance.py", line 73, in ?
c.user = newuser # <-------- changing relation: this bombs out
File "/home/cvogler/src/taubenschlag/aggregator/turbogears/lib/python2.4/SQLAlchemy-0.3.5-py2.4.egg/sqlalchemy/orm/attributes.py", line 42, in __set__
self.set(None, obj, value)
File "/home/cvogler/src/taubenschlag/aggregator/turbogears/lib/python2.4/SQLAlchemy-0.3.5-py2.4.egg/sqlalchemy/orm/attributes.py", line 242, in set
ext.set(event or self, obj, value, old)
File "/home/cvogler/src/taubenschlag/aggregator/turbogears/lib/python2.4/SQLAlchemy-0.3.5-py2.4.egg/sqlalchemy/orm/attributes.py", line 502, in set
getattr(oldchild.__class__, self.key).remove(event, oldchild, obj)
File "/home/cvogler/src/taubenschlag/aggregator/turbogears/lib/python2.4/SQLAlchemy-0.3.5-py2.4.egg/sqlalchemy/orm/attributes.py", line 285, in remove
self.get(obj).remove_with_event(value, event)
File "/home/cvogler/src/taubenschlag/aggregator/turbogears/lib/python2.4/SQLAlchemy-0.3.5-py2.4.egg/sqlalchemy/orm/attributes.py", line 411, in remove_with_event
self.data.remove(item)
ValueError: list.remove(x): x not in list
Comments (2)
-
Account Deleted -
repo owner - changed status to resolved
this error is ultimately caused by your "emails" collection receiving an object of type
Companymail
, which since it is managed by a non-polymorphicEmail
mapper, it later loads the instance as a plainEmail
object when negotiating the bi-directional relationship (anEmail
instance distinct from the already-loadedCompanymail
instance). It then fails to remove the oldCompanymail
instance from the originalUser
since its theEmail
instance that is actually in the collection.To detect the incorrect loading of the
Companymail
as anEmail
is impossible, since nothing about the row identifies it as aCompanymail
row and would require that the correct instance already be loaded in the session, which we cant assume is the case (and would be an extremely expensive operation anyway).So the only point at which this issue can be reasonably detected is at flush time, so I have added a check there. as of changeset:2382 the test program will now raise:
sqlalchemy.exceptions.FlushError: Attempting to flush an item of type <class '__main__.Companymail'> on collection 'User.email (Email)', which is handled by mapper 'Mapper|Email|xemails' and does not load items of that type. Did you mean to use a polymorphic mapper for this relationship ?
The two mappings which will alleviate this issue are:
polymorphic:
punion = polymorphic_union( {"email":emails.outerjoin(companymail).select(companymail.c.company_id == None), "companies":emails.join(companymail),}, "type", "punion" ) emailmapper = mapper(Email, emails, select_table=punion, polymorphic_identity="email", polymorphic_on=punion.c.type) companymapper = mapper(Companymail, companymail, inherits=emailmapper, polymorphic_identity="companies") usermapper = mapper(User, users, properties = {'email': relation(emailmapper, backref='user')})
Or more easily, just remove the
email
collection (since it wasnt going to load correctly anyway):usermapper = mapper(User, users) emailmapper = mapper(Email, emails, properties={'user':relation(usermapper)}) companymapper = mapper(Companymail, companymail, inherits=emailmapper, )
- Log in to comment
Test case for problem