Very strange how these reports are always in pairs. This recipe has been there 
for several years now, yet literally two days ago, someone submitted a pull 
request pointing out and repairing this issue for the first time.     The 
pullreq has been merged and you can apply the changes it has here: 

https://bitbucket.org/zzzeek/sqlalchemy/commits/d6c60cb2f3b1bf27f10aecf542fc0e3f3f903183








On Jun 19, 2013, at 5:53 PM, Seth P <spadow...@gmail.com> wrote:

> 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 sqlalchemy+unsubscr...@googlegroups.com.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> Visit this group at http://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/groups/opt_out.
>  
>  

-- 
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 sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to