The Versioned mixin described in
http://docs.sqlalchemy.org/en/rel_0_8/orm/examples.html#versioned-objects
(which I renamed VersionedMixin, but is otherwise the same) has what I
would consider an unintuitive and undesirable interaction with backref: if
C references A with a backref, adding a new C object referencing a
particular A object will cause the version number of the target A object to
increment, even though there are no changes to the A table. If the relation
has no backref (as in the relationship from C to B below), then the target
object version number is not incremented, as I would expect. It seems that
the code is effectively using session.is_modified(a) to determine whether
to increment the version number, whereas I would have thought
session.is_modified(a,
include_collections=False) would be more appropriate. Is there some use
case I'm not considering that favors the current behavior?
Thanks,
Seth
from sqlalchemy import Column, Integer, String, ForeignKey, create_engine
from sqlalchemy.ext.declarative.api import declarative_base
from sqlalchemy.orm import sessionmaker, scoped_session, relationship,
backref
from history_meta import VersionedMixin, versioned_session
Base = declarative_base(object)
metadata = Base.metadata
class A(VersionedMixin, Base):
__tablename__ = 'a'
__table_args__ = {}
id = Column(Integer, primary_key=True)
name = Column(String(3))
def __repr__(self):
return "A(id=%d,name='%s',version=%d,cs=%s)" % (self.id, self.name,
self.version, [c.name for c in self.cs])
class B(VersionedMixin, Base):
__tablename__ = 'b'
__table_args__ = {}
id = Column(Integer, primary_key=True)
name = Column(String(3))
def __repr__(self):
return "B(id=%d,name='%s',version=%d)" % (self.id, self.name,
self.version)
class C(VersionedMixin, Base):
__tablename__ = 'c'
__table_args__ = {}
id = Column(Integer, primary_key=True)
name = Column(String(3))
a_id = Column(Integer, ForeignKey('a.id'))
a_re = relationship(A, backref='cs')
b_id = Column(Integer, ForeignKey('b.id'))
b_re = relationship(B)
if __name__ == '__main__':
engine = create_engine('sqlite:///:memory:', echo=False)
metadata.create_all(bind=engine)
Session = scoped_session(sessionmaker(bind=engine))
versioned_session(Session)
session = Session()
# populate tables with a single entry in each table
a = A(name='a')
b = B(name='b')
c1 = C(name='c1', a_re=a, b_re=b)
session.add_all([a, b, c1])
session.commit()
print '\nAfter initial commit'
print 'a=%s; is_modified(a)=%s; is_modified(a,
include_collections=False)=%s' % (a, session.is_modified(a),
session.is_modified(a, include_collections=False))
print 'b=%s; is_modified(b)=%s; is_modified(b,
include_collections=False)=%s' % (b, session.is_modified(b),
session.is_modified(b, include_collections=False))
# add another entry in b that points to a
c2 = C(name='c2', a_re=a, b_re=b)
session.add(c2)
print "\nAfter adding C(name='c2', a_re=a, b_re=b), but before
committing:"
print 'a=%s; is_modified(a)=%s; is_modified(a,
include_collections=False)=%s' % (a, session.is_modified(a),
session.is_modified(a, include_collections=False))
print 'b=%s; is_modified(b)=%s; is_modified(b,
include_collections=False)=%s' % (b, session.is_modified(b),
session.is_modified(b, include_collections=False))
session.commit()
print '\nAfter final commit:'
print 'a=%s; is_modified(a)=%s; is_modified(a,
include_collections=False)=%s' % (a, session.is_modified(a),
session.is_modified(a, include_collections=False))
print 'b=%s; is_modified(b)=%s; is_modified(b,
include_collections=False)=%s' % (b, session.is_modified(b),
session.is_modified(b, include_collections=False))
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/groups/opt_out.