flask-session icon indicating copy to clipboard operation
flask-session copied to clipboard

Fix init & multiple app issues with SQLAlchemy interface

Open rsyring opened this issue 10 years ago • 11 comments

These are some proposed fixes to issues I have run into with integrating Flask-Session into our application. The two problems were:

  1. the call to db.create_all() was causing problems as described here: https://github.com/fengsp/flask-session/issues/11
  2. When instantiating an application multiple times in the same process (in our case, during testing) the method used to create the SA table/model would fail b/c it already existed. But a check in for this so it's only created once for each DB.

rsyring avatar Jun 11 '15 13:06 rsyring

@fengsp it would be very helpful to have this fixed. What can I do to get it moving?

nZac avatar Apr 26 '16 15:04 nZac

@rsyring We need to call the create_all. Otherwise, how to do that for users? The session table detail is hidden. Any good ideas?

fengsp avatar Apr 28 '16 08:04 fengsp

@fengsp my suggestion would be to create an entities.py file, and create a SessionMixin class which the user of library could include in their own SQLA metadata / entities however they want.

You might also provide an alembic script in the docs with a raw sql version if developers are trying to migrate.

Flask-Session would then, as part of the init_app be handed the entity which should be used to construct a session object.

nZac avatar Apr 28 '16 13:04 nZac

The create_all call is not necessary, we deleted it in our branch because it was problematic.

revmischa avatar Nov 08 '16 06:11 revmischa

Instead of the create_all() call you could use Session.__table__.create(bind=db.engine, checkfirst=True). That will create the table for the session model if it doesn't already exist.

kwiersma avatar Mar 01 '17 15:03 kwiersma

moving to flask-session 0.3.1 from 0.3.0 made the flask_session table stop getting created when doing my initial alembic schema migration. something changed that broke our use case. how are we supposed to deploy the flask_session table now when doing an initial schema migration/deploy?

revmischa avatar Apr 05 '17 19:04 revmischa

@revmischa You should do the db creation by yourself, including your other models.

fengsp avatar Apr 06 '17 02:04 fengsp

The fix for recreating the session model is still very needed. I have to fork this repo in order to do tests, or else I get errors from the test suite as it tries to redefine this table on every initialization of the plugin

tassaron avatar Jan 01 '21 13:01 tassaron

I am using 0.4.0, and am still having this problem:

-- I have around 396 pytest cases. For 3 ( three ) particular test cases, if I run only them together, two ( 2 ) would fail.

I came to this post,

https://stackoverflow.com/questions/64721498/how-do-you-resolve-already-defined-in-this-metadata-instance-error-with-flask How do you resolve 'Already defined in this MetaData Instance' Error with Flask Pytest, SqlAlchemy, and Flask Sessions?

I saw @tassaron answer. If I modified flask_session/sessions.py directly, the way @tassaron does:


class SqlAlchemySessionInterface(SessionInterface):
...
    def __init__(self, app, db, table, key_prefix, use_signer=False,
                 permanent=True):
...
        if table not in self.db.metadata:
            class Session(self.db.Model):
...
            # self.db.create_all()
            self.sql_session_model = db.session_ext_session_model = Session
        else:
            self.sql_session_model = db.session_ext_session_model

My problems go away. We do need:

        else:
            self.sql_session_model = db.session_ext_session_model

Otherwise later on it would complain self.sql_session_model not exist.

Thank you and best regards.

...behai.

behai-nguyen avatar Nov 23 '22 14:11 behai-nguyen

This is how I get around this problem. It feels like a hack though:

"""
Flask-Session version 0.4.0 -- latest as on 24/November/2022.

Work around / hack for getting around the exception during tests:

sqlalchemy.exc.InvalidRequestError: Table 'sessions' is already defined 
for this MetaData instance. Specify 'extend_existing=True' to redefine 
options and columns on an existing Table object.

Usage:

try:
    from book_keeping.library.fixed_session import FixedSession as Session
except ImportError:
    from flask_session import Session


References:

https://stackoverflow.com/questions/64721498/how-do-you-resolve-already-defined-in-this-metadata-instance-error-with-flask
https://github.com/fengsp/flask-session/blob/1c1f7903184673682bd1d75432c8f455b62393a4/flask_session/sessions.py
https://github.com/tassaron/muffin-shop/blob/main/src/helpers/main/session_interface.py
https://github.com/fengsp/flask-session/pull/12
"""
from flask_session.sessions import SqlAlchemySessionInterface
from flask_session import Session

class FixedSqlAlchemySessionInterface( SqlAlchemySessionInterface ):
    def __init__(self, app, db, table, key_prefix, use_signer=False,
                 permanent=True):
        """
        Assumption: the way I use it, db is always a valid instance 
        at this point.
        """
        if table not in db.metadata:
            super().__init__( app, db, table, key_prefix, use_signer, permanent )
            db.session_ext_session_model = self.sql_session_model
        else:
            # print( "`sessions` table already exists..." )

            self.db = db
            self.key_prefix = key_prefix
            self.use_signer = use_signer
            self.permanent = permanent
            self.has_same_site_capability = hasattr(self, "get_cookie_samesite")

            self.sql_session_model = db.session_ext_session_model
            
class FixedSession( Session ):
    def _get_interface(self, app):
        config = app.config.copy()

        if config[ 'SESSION_TYPE' ] != 'sqlalchemy':
            return super()._get_interface( app )

        else:
            config.setdefault( 'SESSION_PERMANENT', True )
            config.setdefault( 'SESSION_KEY_PREFIX', 'session:' )

            return FixedSqlAlchemySessionInterface(
                app, config['SESSION_SQLALCHEMY'],
                config['SESSION_SQLALCHEMY_TABLE'],
                config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
                config['SESSION_PERMANENT'] )

behai-nguyen avatar Nov 24 '22 00:11 behai-nguyen

Hi @rsyring @tassaron @behai-nguyen this repo is under new maintainers. Would be grateful if you can test 0.6.0 and confirm if this fixed your issues.

Lxstr avatar Feb 02 '24 04:02 Lxstr

Table option keep_existing has been used to fix this and prevent accidental schema changes from 0.6.0

Lxstr avatar Feb 25 '24 13:02 Lxstr