Skip to content

Commit 0d2965c

Browse files
committed
Merge pull request pallets-eco#109 from paxan/bugfix/record-ops-only-for-signalling-sessions
Record operations only for signalling sessions
2 parents 2e029ac + cf40cae commit 0d2965c

File tree

2 files changed

+40
-2
lines changed

2 files changed

+40
-2
lines changed

flask_sqlalchemy.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,12 @@ def after_update(self, mapper, connection, instance):
166166
return self._record(mapper, instance, 'update')
167167

168168
def _record(self, mapper, model, operation):
169-
pk = tuple(mapper.primary_key_from_instance(model))
170-
orm.object_session(model)._model_changes[pk] = (model, operation)
169+
s = orm.object_session(model)
170+
# Skip the operation tracking when a non signalling session
171+
# is used.
172+
if isinstance(s, _SignallingSessionExtension):
173+
pk = tuple(mapper.primary_key_from_instance(model))
174+
s._model_changes[pk] = (model, operation)
171175
return EXT_CONTINUE
172176

173177

test_sqlalchemy.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from datetime import datetime
77
import flask
88
from flask.ext import sqlalchemy
9+
from sqlalchemy.orm import sessionmaker
910

1011

1112
def make_todo_model(db):
@@ -414,6 +415,38 @@ class FOOBar(db.Model):
414415
db.session.add(fb)
415416
assert fb not in db.session # because a new scope is generated on each call
416417

418+
class StandardSessionTestCase(unittest.TestCase):
419+
def test_insert_update_delete(self):
420+
# Ensure _SignalTrackingMapperExtension doesn't croak when
421+
# faced with a vanilla SQLAlchemy session.
422+
#
423+
# Verifies that "AttributeError: 'SessionMaker' object has no attribute '_model_changes'"
424+
# is not thrown.
425+
app = flask.Flask(__name__)
426+
app.config['SQLALCHEMY_ENGINE'] = 'sqlite://'
427+
app.config['TESTING'] = True
428+
db = sqlalchemy.SQLAlchemy(app)
429+
Session = sessionmaker(bind=db.engine)
430+
431+
class QazWsx(db.Model):
432+
id = db.Column(db.Integer, primary_key=True)
433+
x = db.Column(db.String, default='')
434+
435+
db.create_all()
436+
session = Session()
437+
session.add(QazWsx())
438+
session.flush() # issues an INSERT.
439+
session.expunge_all()
440+
qaz_wsx = session.query(QazWsx).first()
441+
assert qaz_wsx.x == ''
442+
qaz_wsx.x = 'test'
443+
session.flush() # issues an UPDATE.
444+
session.expunge_all()
445+
qaz_wsx = session.query(QazWsx).first()
446+
assert qaz_wsx.x == 'test'
447+
session.delete(qaz_wsx) # issues a DELETE.
448+
assert session.query(QazWsx).first() is None
449+
417450

418451
class CommitOnTeardownTestCase(unittest.TestCase):
419452

@@ -463,6 +496,7 @@ def suite():
463496
suite.addTest(unittest.makeSuite(CommitOnTeardownTestCase))
464497
if flask.signals_available:
465498
suite.addTest(unittest.makeSuite(SignallingTestCase))
499+
suite.addTest(unittest.makeSuite(StandardSessionTestCase))
466500
return suite
467501

468502

0 commit comments

Comments
 (0)