Hello all:

We are using examples/versioned_history (i.e. history_meta.py)

https://github.com/zzzeek/sqlalchemy/blob/master/examples/versioned_history/history_meta.py

We noticed the following behavior:

Within the same transaction (i.e. pyramid_tm), whenever a Versioned object 
changes and its followed by a session.query(), the event 'before_flush' is 
triggered and a new history object is created accompanied by a version 
increase. However, subsequent changes to the same versioned object followed 
by more session.query() keep adding new history objects with versions 
increased even though the transaction has not finished. 

Shouldn't all changes made to the same versioned object be "coalesced" into 
a single new history object and a single version increase if they all 
happen within the same transaction?

It seems like as a side-effect of calling session.query() interlaced with 
versioned object changes would increase the version within the same 
transaction. Wouldn't this be considered a "questionable side-effect"?

Here is the modified "PASSING" test case depicting the multiple version 
increase "side-effect":

# examples/versioned_history/test_versioning.py
# 
https://github.com/zzzeek/sqlalchemy/blob/master/examples/versioned_history/test_versioning.py

    def test_relationship(self):


        class SomeRelated(self.Base, ComparableEntity):

            __tablename__ = 'somerelated'


            id = Column(Integer, primary_key=True)


        class SomeClass(Versioned, self.Base, ComparableEntity):

            __tablename__ = 'sometable'


            id = Column(Integer, primary_key=True)

            name = Column(String(50))

            related_id = Column(Integer, ForeignKey('somerelated.id'))

            related = relationship("SomeRelated", backref='classes')


        SomeClassHistory = SomeClass.__history_mapper__.class_


        self.create_tables()

        sess = self.session

        sc = SomeClass(name='sc1')

        sess.add(sc)

        sess.commit()


        assert sc.version == 1


        sr1 = SomeRelated()

        sc.related = sr1 


        sr2 = SomeRelated() # extra one to be used later

        sess.commit()


        assert sc.version == 2


        eq_(

            sess.query(SomeClassHistory).filter(

                SomeClassHistory.version == 1).all(),

            [SomeClassHistory(version=1, name='sc1', related_id=None)]

        )   


        sc.related = None


        eq_(

            sess.query(SomeClassHistory).order_by(

                SomeClassHistory.version).all(),

            [   

                SomeClassHistory(version=1, name='sc1', related_id=None),

                SomeClassHistory(version=2, name='sc1', related_id=sr1.id)

            ]   

        )   


        assert sc.version == 3


        #### Modifications start here:


        sc.related = sr2 # Change made


        assert sc.version == 3 # Version has not change yet ... but it will 
since there is a change now


        history = 
sess.query(SomeClassHistory).order_by(SomeClassHistory.version).all() # 
before_flush triggered, new history object added to session

        for h in history:

            print "%r" % (h) 


        assert sc.version == 4 # Version increased after session.query


        eq_(

            sess.query(SomeClassHistory).order_by(

                SomeClassHistory.version).all(),

            [   

                SomeClassHistory(version=1, name='sc1', related_id=None),

                SomeClassHistory(version=2, name='sc1', related_id=sr1.id),

                SomeClassHistory(version=3, name='sc1', related_id=None),

            ]

        )


        sc.related = None # Change made


        assert sc.version == 4 # Version didn't increase ... yet


        eq_(

            sess.query(SomeClassHistory).order_by(

                SomeClassHistory.version).all(),

            [

                SomeClassHistory(version=1, name='sc1', related_id=None),

                SomeClassHistory(version=2, name='sc1', related_id=sr1.id),

                SomeClassHistory(version=3, name='sc1', related_id=None),

                SomeClassHistory(version=4, name='sc1', related_id=sr2.id),

            ]

        )


        assert sc.version == 5 # Version increased again although there was 
no net change within this transaction (sc.related = None => sc.related = 
sr2 => sc.related = None)


        # Notice there were no session.commit()'s 


        # commit all now

        sess.commit()

        assert sc.version == 5   # version increased twice in a single 
transaction


        scr = sess.query(SomeClass).get(sc.id)

        assert scr.version == 5


        # See final history in log

        print "Final history"

        history = 
sess.query(SomeClassHistory).order_by(SomeClassHistory.version).all()

        for h in history:

            print "%r" % (h) 


        assert False, "Test case forced to fail"



Here is the output log:


======================================================================

FAIL: test_relationship 
(examples.versioned_history.test_versioning.TestVersioning)

----------------------------------------------------------------------

Traceback (most recent call last):

  File 
"/Users/henddher/Documents/python_workspace/github_zzzeek_sqlalchemy/examples/versioned_history/test_versioning.py",
 
line 628, in test_relationship

    assert False, "Test case forced to fail"

AssertionError: Test case forced to fail

-------------------- >> begin captured stdout << ---------------------

SomeClassHistory(changed=datetime.datetime(2016, 9, 2, 17, 32, 34, 440061), 
id=1, name=u'sc1', related_id=None, version=1)

SomeClassHistory(changed=datetime.datetime(2016, 9, 2, 17, 32, 34, 445386), 
id=1, name=u'sc1', related_id=1, version=2)

SomeClassHistory(changed=datetime.datetime(2016, 9, 2, 17, 32, 34, 448406), 
id=1, name=u'sc1', related_id=None, version=3)

Final history

SomeClassHistory(changed=datetime.datetime(2016, 9, 2, 17, 32, 34, 440061), 
id=1, name=u'sc1', related_id=None, version=1)

SomeClassHistory(changed=datetime.datetime(2016, 9, 2, 17, 32, 34, 445386), 
id=1, name=u'sc1', related_id=1, version=2)

SomeClassHistory(changed=datetime.datetime(2016, 9, 2, 17, 32, 34, 448406), 
id=1, name=u'sc1', related_id=None, version=3)

SomeClassHistory(changed=datetime.datetime(2016, 9, 2, 17, 32, 34, 451971), 
id=1, name=u'sc1', related_id=2, version=4)


--------------------- >> end captured stdout << ----------------------

-------------------- >> begin captured logging << --------------------

sqlalchemy.engine.base.Engine: INFO: PRAGMA table_info("sometable_history")

sqlalchemy.engine.base.Engine: INFO: ()

sqlalchemy.engine.base.Engine: INFO: PRAGMA table_info("somerelated")

sqlalchemy.engine.base.Engine: INFO: ()

sqlalchemy.engine.base.Engine: INFO: PRAGMA table_info("sometable")

sqlalchemy.engine.base.Engine: INFO: ()

sqlalchemy.engine.base.Engine: INFO: 

CREATE TABLE sometable_history (

id INTEGER NOT NULL, 

name VARCHAR(50), 

related_id INTEGER, 

version INTEGER NOT NULL, 

changed DATETIME, 

PRIMARY KEY (id, version)

)



sqlalchemy.engine.base.Engine: INFO: ()

sqlalchemy.engine.base.Engine: INFO: COMMIT

sqlalchemy.engine.base.Engine: INFO: 

CREATE TABLE somerelated (

id INTEGER NOT NULL, 

PRIMARY KEY (id)

)



sqlalchemy.engine.base.Engine: INFO: ()

sqlalchemy.engine.base.Engine: INFO: COMMIT

sqlalchemy.engine.base.Engine: INFO: 

CREATE TABLE sometable (

id INTEGER NOT NULL, 

name VARCHAR(50), 

related_id INTEGER, 

version INTEGER NOT NULL, 

PRIMARY KEY (id), 

FOREIGN KEY(related_id) REFERENCES somerelated (id)

)



sqlalchemy.engine.base.Engine: INFO: ()

sqlalchemy.engine.base.Engine: INFO: COMMIT

sqlalchemy.engine.base.Engine: INFO: BEGIN (implicit)

sqlalchemy.engine.base.Engine: INFO: INSERT INTO sometable (name, 
related_id, version) VALUES (?, ?, ?)

sqlalchemy.engine.base.Engine: INFO: ('sc1', None, 1)

sqlalchemy.engine.base.Engine: INFO: COMMIT

sqlalchemy.engine.base.Engine: INFO: BEGIN (implicit)

sqlalchemy.engine.base.Engine: INFO: SELECT sometable.id AS sometable_id, 
sometable.name AS sometable_name, sometable.related_id AS 
sometable_related_id, sometable.version AS sometable_version 

FROM sometable 

WHERE sometable.id = ?

sqlalchemy.engine.base.Engine: INFO: (1,)

sqlalchemy.engine.base.Engine: INFO: INSERT INTO somerelated DEFAULT VALUES

sqlalchemy.engine.base.Engine: INFO: ()

sqlalchemy.engine.base.Engine: INFO: INSERT INTO sometable_history (id, 
name, related_id, version, changed) VALUES (?, ?, ?, ?, ?)

sqlalchemy.engine.base.Engine: INFO: (1, u'sc1', None, 1, '2016-09-02 
17:32:34.440061')

sqlalchemy.engine.base.Engine: INFO: UPDATE sometable SET related_id=?, 
version=? WHERE sometable.id = ?

sqlalchemy.engine.base.Engine: INFO: (1, 2, 1)

sqlalchemy.engine.base.Engine: INFO: COMMIT

sqlalchemy.engine.base.Engine: INFO: BEGIN (implicit)

sqlalchemy.engine.base.Engine: INFO: SELECT sometable.id AS sometable_id, 
sometable.name AS sometable_name, sometable.related_id AS 
sometable_related_id, sometable.version AS sometable_version 

FROM sometable 

WHERE sometable.id = ?

sqlalchemy.engine.base.Engine: INFO: (1,)

sqlalchemy.engine.base.Engine: INFO: SELECT sometable_history.id AS 
sometable_history_id, sometable_history.name AS sometable_history_name, 
sometable_history.related_id AS sometable_history_related_id, 
sometable_history.version AS sometable_history_version, 
sometable_history.changed AS sometable_history_changed 

FROM sometable_history 

WHERE sometable_history.version = ?

sqlalchemy.engine.base.Engine: INFO: (1,)

sqlalchemy.engine.base.Engine: INFO: SELECT somerelated.id AS 
somerelated_id 

FROM somerelated 

WHERE somerelated.id = ?

sqlalchemy.engine.base.Engine: INFO: (1,)

sqlalchemy.engine.base.Engine: INFO: INSERT INTO sometable_history (id, 
name, related_id, version, changed) VALUES (?, ?, ?, ?, ?)

sqlalchemy.engine.base.Engine: INFO: (1, u'sc1', 1, 2, '2016-09-02 
17:32:34.445386')

sqlalchemy.engine.base.Engine: INFO: UPDATE sometable SET related_id=?, 
version=? WHERE sometable.id = ?

sqlalchemy.engine.base.Engine: INFO: (None, 3, 1)

sqlalchemy.engine.base.Engine: INFO: SELECT sometable_history.id AS 
sometable_history_id, sometable_history.name AS sometable_history_name, 
sometable_history.related_id AS sometable_history_related_id, 
sometable_history.version AS sometable_history_version, 
sometable_history.changed AS sometable_history_changed 

FROM sometable_history ORDER BY sometable_history.version

sqlalchemy.engine.base.Engine: INFO: ()

sqlalchemy.engine.base.Engine: INFO: INSERT INTO somerelated DEFAULT VALUES

sqlalchemy.engine.base.Engine: INFO: ()

sqlalchemy.engine.base.Engine: INFO: INSERT INTO sometable_history (id, 
name, related_id, version, changed) VALUES (?, ?, ?, ?, ?)

sqlalchemy.engine.base.Engine: INFO: (1, u'sc1', None, 3, '2016-09-02 
17:32:34.448406')

sqlalchemy.engine.base.Engine: INFO: UPDATE sometable SET related_id=?, 
version=? WHERE sometable.id = ?

sqlalchemy.engine.base.Engine: INFO: (2, 4, 1)

sqlalchemy.engine.base.Engine: INFO: SELECT sometable_history.id AS 
sometable_history_id, sometable_history.name AS sometable_history_name, 
sometable_history.related_id AS sometable_history_related_id, 
sometable_history.version AS sometable_history_version, 
sometable_history.changed AS sometable_history_changed 

FROM sometable_history ORDER BY sometable_history.version

sqlalchemy.engine.base.Engine: INFO: ()

sqlalchemy.engine.base.Engine: INFO: SELECT sometable_history.id AS 
sometable_history_id, sometable_history.name AS sometable_history_name, 
sometable_history.related_id AS sometable_history_related_id, 
sometable_history.version AS sometable_history_version, 
sometable_history.changed AS sometable_history_changed 

FROM sometable_history ORDER BY sometable_history.version

sqlalchemy.engine.base.Engine: INFO: ()

sqlalchemy.engine.base.Engine: INFO: INSERT INTO sometable_history (id, 
name, related_id, version, changed) VALUES (?, ?, ?, ?, ?)

sqlalchemy.engine.base.Engine: INFO: (1, u'sc1', 2, 4, '2016-09-02 
17:32:34.451971')

sqlalchemy.engine.base.Engine: INFO: UPDATE sometable SET related_id=?, 
version=? WHERE sometable.id = ?

sqlalchemy.engine.base.Engine: INFO: (None, 5, 1)

sqlalchemy.engine.base.Engine: INFO: SELECT sometable_history.id AS 
sometable_history_id, sometable_history.name AS sometable_history_name, 
sometable_history.related_id AS sometable_history_related_id, 
sometable_history.version AS sometable_history_version, 
sometable_history.changed AS sometable_history_changed 

FROM sometable_history ORDER BY sometable_history.version

sqlalchemy.engine.base.Engine: INFO: ()

sqlalchemy.engine.base.Engine: INFO: COMMIT

sqlalchemy.engine.base.Engine: INFO: BEGIN (implicit)

sqlalchemy.engine.base.Engine: INFO: SELECT sometable.id AS sometable_id, 
sometable.name AS sometable_name, sometable.related_id AS 
sometable_related_id, sometable.version AS sometable_version 

FROM sometable 

WHERE sometable.id = ?

sqlalchemy.engine.base.Engine: INFO: (1,)

sqlalchemy.engine.base.Engine: INFO: SELECT sometable_history.id AS 
sometable_history_id, sometable_history.name AS sometable_history_name, 
sometable_history.related_id AS sometable_history_related_id, 
sometable_history.version AS sometable_history_version, 
sometable_history.changed AS sometable_history_changed 

FROM sometable_history ORDER BY sometable_history.version

sqlalchemy.engine.base.Engine: INFO: ()

--------------------- >> end captured logging << ---------------------



-- 
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 https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to