--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmonitor/README.md Sat Jan 11 23:41:32 2014 +0900
+One of the new features in SQLAlchemy 0.9 is event removal API. Basically, if
+you've added an event listener, this change makes you able to remove it.
+Although, to be honest, I suspect there was a way to remove event listeners
+before 0.9, but it was probably complicated or undocumented.
+With this change you can now do a context manager that can watch queries
+executed for an orbitrary (small) block of code without leaving any event
+listeners behind or catching unrelated queries by accident.
+Anyway, here's some code, and if you need a complete example, see example.py.
+ class QMonitor(object):
+ def __init__(self, max_queries):
+ self.max_queries = max_queries
+ """ User requires assistance in query minimization.
+ qc = Counter(zip(*self.queries)[0])
+ if qc.values().count(1) < len(qc):
+ '** Repeated {0} times: {1}'.format(c, q)
+ print (u'Too many queries, try eliminating duplicates:\n' +
+ u'\n'.join(duplicates))
+ print u'Too many queries:\n' + u'\n'.join(zip(*self.queries)[0])
+ """ Check saved database queries.
+ if len(self.queries) > self.max_queries:
+ def qmonitor(max_queries=15):
+ qm = QMonitor(max_queries)
+ def before_cursor_execute(conn, cursor, statement, parameters, context, executemany):
+ context._query_start_time = time.time()
+ def after_cursor_execute(conn, cursor, statement, parameters, context, executemany):
+ total = time.time() - context._query_start_time
+ qm.queries.append((statement, total))
+ event.listen(engine, 'before_cursor_execute', before_cursor_execute)
+ event.listen(engine, 'after_cursor_execute', after_cursor_execute)
+ event.remove(engine, 'before_cursor_execute', before_cursor_execute)
+ event.remove(engine, 'after_cursor_execute', after_cursor_execute)
+ q = session.query(Address)
+ with qmonitor(max_queries=1) as qm:
+ print address.email_address + ' - ' + address.user.fullname
+This will check performace (so far just count number of DB queries) and assist
+in making the "managed" block of code as fast as possible. It is also quite
+easy to use in integration tests for finding database-intensive code.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmonitor/example.py Sat Jan 11 23:41:32 2014 +0900
+from collections import Counter
+from contextlib import contextmanager
+from sqlalchemy import create_engine
+from sqlalchemy import event
+from sqlalchemy import Column, ForeignKey, Integer, String
+from sqlalchemy.orm import relationship, backref, joinedload
+from sqlalchemy.orm import sessionmaker
+from sqlalchemy.ext.declarative import declarative_base
+engine = create_engine('sqlite:///:memory:')
+Session = sessionmaker(bind=engine)
+Base = declarative_base()
+ __tablename__ = 'users'
+ id = Column(Integer, primary_key=True)
+ fullname = Column(String)
+ password = Column(String)
+ __tablename__ = 'addresses'
+ id = Column(Integer, primary_key=True)
+ email_address = Column(String, nullable=False)
+ user_id = Column(Integer, ForeignKey('users.id'))
+ user = relationship("User", backref=backref('addresses', order_by=id))
+Base.metadata.create_all(engine)
+ User(name='wendy', fullname='Wendy Williams', password='foobar'),
+ User(name='mary', fullname='Mary Contrary', password='xxg527'),
+ User(name='fred', fullname='Fred Flinstone', password='blah')
+ Address(email_address=user.name + '@example.com'),
+ Address(email_address=user.name + '@work.corp.com')
+ def __init__(self, max_queries):
+ self.max_queries = max_queries
+ """ User requires assistance in query minimization.
+ qc = Counter(zip(*self.queries)[0])
+ if qc.values().count(1) < len(qc):
+ '** Repeated {0} times: {1}'.format(c, q)
+ print (u'Too many queries, try eliminating duplicates:\n' +
+ u'\n'.join(duplicates))
+ print u'Too many queries:\n' + u'\n'.join(zip(*self.queries)[0])
+ """ Check saved database queries.
+ if len(self.queries) > self.max_queries:
+def qmonitor(max_queries=15):
+ qm = QMonitor(max_queries)
+ def before_cursor_execute(conn, cursor, statement, parameters, context, executemany):
+ context._query_start_time = time.time()
+ def after_cursor_execute(conn, cursor, statement, parameters, context, executemany):
+ total = time.time() - context._query_start_time
+ qm.queries.append((statement, total))
+ event.listen(engine, 'before_cursor_execute', before_cursor_execute)
+ event.listen(engine, 'after_cursor_execute', after_cursor_execute)
+ event.remove(engine, 'before_cursor_execute', before_cursor_execute)
+ event.remove(engine, 'after_cursor_execute', after_cursor_execute)
+q = session.query(Address)
+with qmonitor(max_queries=1) as qm:
+ print address.email_address + ' - ' + address.user.fullname
+print '== Optimized query'
+q = session.query(Address).options(joinedload('user'))
+with qmonitor(max_queries=1) as qm:
+ print address.email_address + ' - ' + address.user.fullname