diff --git a/docs/panels.rst b/docs/panels.rst index 1c9252d..a959be1 100644 --- a/docs/panels.rst +++ b/docs/panels.rst @@ -72,10 +72,14 @@ Shows SQL queries run during the current request. For additional details on query recording see the :py:func:`~flask_sqlalchemy.get_debug_queries` documentation. + If you use raw sqlalchemy with `Flask-SQLAlchemy-Session`_ pass the engine + in like DebugToolbarExtension(app, sqlalchemy_engine=engine) + + .. image:: _static/screenshot-sqlalchemy-panel.png .. _Flask-SQLAlchemy: http://flask-sqlalchemy.pocoo.org/ - +.. _Flask-SQLAlchemy-Session: https://pypi.python.org/pypi/Flask-SQLAlchemy-Session Logging ------- diff --git a/flask_debugtoolbar/__init__.py b/flask_debugtoolbar/__init__.py index 80c896d..7889ae1 100644 --- a/flask_debugtoolbar/__init__.py +++ b/flask_debugtoolbar/__init__.py @@ -41,8 +41,9 @@ class DebugToolbarExtension(object): _redirect_codes = [301, 302, 303, 304] - def __init__(self, app=None): + def __init__(self, app=None, sqlalchemy_engine=None): self.app = app + self.engine = sqlalchemy_engine self.debug_toolbars = {} # Configure jinja for the internal templates and add url rules @@ -80,8 +81,19 @@ def init_app(self, app): app.add_url_rule('/_debug_toolbar/static/', '_debug_toolbar.static', self.send_static_file) - - app.register_blueprint(module, url_prefix='/_debug_toolbar/views') + if self.engine: + # use 'internal' API from flask_sqlalchemy to install events. + # maybe re-implement this for flask-debugtoolbar + from flask_sqlalchemy import _EngineDebuggingSignalEvents, _record_queries + if _record_queries(app): + _EngineDebuggingSignalEvents( + engine=self.engine, + import_name=app.import_name).register() + + app.register_blueprint( + module, + url_prefix='/_debug_toolbar/views', + sqlalchemy_engine=self.engine) def _default_config(self, app): return { diff --git a/flask_debugtoolbar/panels/sqlalchemy.py b/flask_debugtoolbar/panels/sqlalchemy.py index ded1c10..b356436 100644 --- a/flask_debugtoolbar/panels/sqlalchemy.py +++ b/flask_debugtoolbar/panels/sqlalchemy.py @@ -12,6 +12,19 @@ from flask_debugtoolbar.utils import format_fname, format_sql import itsdangerous +_engine = None + +@module.record_once +def store_engine(state): + global _engine + _engine = state.options.get('sqlalchemy_engine') + + +def get_engine(): + if _engine is not None: + return _engine + elif sqlalchemy_available: + return SQLAlchemy().get_engine(current_app) _ = lambda x: x @@ -60,7 +73,7 @@ def recording_enabled(): def is_available(): return (json_available and sqlalchemy_available - and extension_used() and recording_enabled()) + and (extension_used() or (_engine is not None)) and recording_enabled()) def get_queries(): @@ -110,6 +123,7 @@ def content(self): return self.render('panels/sqlalchemy_error.html', { 'json_available': json_available, 'sqlalchemy_available': sqlalchemy_available, + 'sqlalchemy_engine_available': _engine is not None, 'extension_used': extension_used(), 'recording_enabled': recording_enabled(), }) @@ -125,6 +139,7 @@ def content(self): }) return self.render('panels/sqlalchemy.html', {'queries': data}) + # Panel views @@ -133,7 +148,9 @@ def content(self): defaults=dict(explain=True)) def sql_select(explain=False): statement, params = load_query(request.args['query']) - engine = SQLAlchemy().get_engine(current_app) + engine = get_engine() + if engine is None: + return 'No SQLAlchemy engine has been configured.' if explain: if engine.driver == 'pysqlite': diff --git a/test/basic_app_plain_sqlalchemy.py b/test/basic_app_plain_sqlalchemy.py new file mode 100644 index 0000000..43277db --- /dev/null +++ b/test/basic_app_plain_sqlalchemy.py @@ -0,0 +1,35 @@ +from flask import Flask, render_template +from flask_debugtoolbar import DebugToolbarExtension +from sqlalchemy import Column, Integer, create_engine +from sqlalchemy.orm import Session +from sqlalchemy.ext.declarative import declarative_base + +app = Flask('basic_app_plain_sqlalchemy') +app.debug = True +app.config['SECRET_KEY'] = 'abc123' + +# make sure these are printable in the config panel +app.config['BYTES_VALUE'] = b'\x00' +app.config['UNICODE_VALUE'] = u'\uffff' + + +engine = create_engine('sqlite://') +toolbar = DebugToolbarExtension(app, sqlalchemy_engine=engine) + + +Base = declarative_base() +class Foo(Base): + __tablename__ = 'foo' + id = Column(Integer, primary_key=True) + + +@app.route('/') +def index(): + Base.metadata.create_all(engine) + session = Session(engine) + session.query(Foo).filter_by(id=1).all() + return render_template('basic_app.html') + + +if __name__ == '__main__': + app.run() \ No newline at end of file