Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 31 additions & 18 deletions flask_sqlalchemy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
:copyright: (c) 2014 by Armin Ronacher, Daniel Neuhäuser.
:license: BSD, see LICENSE for more details.
"""
from __future__ import with_statement, absolute_import
from __future__ import absolute_import
import os
import re
import sys
Expand Down Expand Up @@ -148,7 +148,7 @@ class SignallingSession(SessionBase):
to an external transaction.
"""

def __init__(self, db, autocommit=False, autoflush=True, app=None, **options):
def __init__(self, db, autocommit=False, autoflush=True, **options):
#: The application that this session belongs to.
self.app = app = db.get_app()
track_modifications = app.config['SQLALCHEMY_TRACK_MODIFICATIONS']
Expand Down Expand Up @@ -729,14 +729,9 @@ class to be used in place of :class:`Model`.
def __init__(self, app=None, use_native_unicode=True, session_options=None,
metadata=None, query_class=BaseQuery, model_class=Model):

if session_options is None:
session_options = {}

session_options.setdefault('scopefunc', connection_stack.__ident_func__)
session_options.setdefault('query_cls', query_class)
self.use_native_unicode = use_native_unicode
self.session = self.create_scoped_session(session_options)
self.Query = query_class
self.session = self.create_scoped_session(session_options)
self.Model = self.make_declarative_base(model_class, metadata)
self._engine_lock = Lock()
self.app = app
Expand All @@ -747,26 +742,44 @@ def __init__(self, app=None, use_native_unicode=True, session_options=None,

@property
def metadata(self):
"""Returns the metadata"""
"""The metadata associated with ``db.Model``."""

return self.Model.metadata

def create_scoped_session(self, options=None):
"""Helper factory method that creates a scoped session. It
internally calls :meth:`create_session`.
"""Create a :class:`~sqlalchemy.orm.scoping.scoped_session`
on the factory from :meth:`create_session`.

An extra key ``'scopefunc'`` can be set on the ``options`` dict to specify a custom
scope function. If it's not provided, Flask's app context stack identity is used.
This will ensure that sessions are created and removed with the request/response cycle,
and should be fine in most cases.

:param options: dict of keyword arguments passed to session class in ``create_session``
"""

if options is None:
options = {}
scopefunc = options.pop('scopefunc', None)
return orm.scoped_session(partial(self.create_session, options),
scopefunc=scopefunc)

scopefunc = options.pop('scopefunc', connection_stack.__ident_func__)
options.setdefault('query_cls', self.Query)
return orm.scoped_session(self.create_session(options), scopefunc=scopefunc)

def create_session(self, options):
"""Creates the session. The default implementation returns a
:class:`SignallingSession`.
"""Create the session factory used by :meth:`create_scoped_session`.

The factory **must** return an object that SQLAlchemy recognizes as a session,
or registering session events may raise an exception.

.. versionadded:: 2.0
Valid factories include a :class:`~sqlalchemy.orm.session.Session`
class or a :class:`~sqlalchemy.orm.session.sessionmaker`.

The default implementation creates a ``sessionmaker`` for :class:`SignallingSession`.

:param options: dict of keyword arguments passed to session class
"""
return SignallingSession(self, **options)

return orm.sessionmaker(class_=SignallingSession, db=self, **options)

def make_declarative_base(self, model, metadata=None):
"""Creates the declarative base."""
Expand Down
9 changes: 7 additions & 2 deletions test_sqlalchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from datetime import datetime
import flask
import flask_sqlalchemy as sqlalchemy
from sqlalchemy import MetaData
from sqlalchemy import MetaData, event
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import sessionmaker

Expand Down Expand Up @@ -676,7 +676,6 @@ class FOOBar(db.Model):
assert fb not in db.session # because a new scope is generated on each call



class CommitOnTeardownTestCase(unittest.TestCase):

def setUp(self):
Expand Down Expand Up @@ -744,6 +743,12 @@ class QazWsx(db.Model):
session.delete(qaz_wsx) # issues a DELETE.
assert session.query(QazWsx).first() is None

def test_listen_to_session_event(self):
app = flask.Flask(__name__)
app.config['TESTING'] = True
db = sqlalchemy.SQLAlchemy(app)
event.listen(db.session, 'after_commit', lambda session: None)


def suite():
suite = unittest.TestSuite()
Expand Down