uselist=False collection not backref deferred, leads to unwanted load/autoflush
Issue #2741
resolved
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class A(Base):
__tablename__ = 'a'
id = Column(Integer, primary_key=True)
b = relationship("B", uselist=False, backref="a")
class B(Base):
__tablename__ = 'b'
id = Column(Integer, primary_key=True)
a_id = Column(Integer, ForeignKey('a.id'), nullable=False)
e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)
sess = Session(e)
a1 = A()
b1 = B(a=a1)
sess.add(a1)
a2 = A()
sess.add(a2)
sess.commit()
b1 = sess.query(B).first()
# this is needed to trigger it
b1.a
b1.a = a2
Comments (4)
-
reporter -
reporter - changed status to wontfix
-
reporter we can also get the "multiple rows" condition by just turning off active_history:
A.b.impl.active_history = False b1.a = a2 sess.commit() print a2.b
it might be an interesting feature to allow users to override active_history, right now you can turn it on if not already, but not turn it off at the configuration level.
-
reporter - removed milestone
Removing milestone: 0.8.xx (automated comment)
- Log in to comment
the difference between uselist=True and uselist=False is that the old value must be removed. Plenty of tests in test_backref_mutations test this kind of thing. Suppose we disable the load on backref:
then run the test like this:
a2.b
now has two b's attached andb2
has not been replaced byb1
, we get:in this case, if we stick to the plan and do no_autoflush:
we still get an integrity error, because the replacement of
b2
throws a not null exception, but this is what we want in this case, since uselist=False means the old value must be replaced.