Search code examples
pythonflasksqlalchemyflask-security

How to prevent flask-security from using db_session.query_property()


I'm trying to setup flask-security using the example at https://flask-security-too.readthedocs.io/en/stable/quickstart.html. I am using SQLAlchemy, not flask-sqlalchemy. I was able to get the example to work, but I'm having problems integrating it in my application. flask-security seems to require Base.query = db_session.query_property(), which in turn, seems to require using scoped-session, which I don't use (there seem to be some strong opinions against using it)

This seems to be a pretty weird requirement of flask-security, which appears to be undocumented, except in the sample code. I'm just wondering if this will cause a problem with other parts of my application

There also seems to be some inconsistencies with various examples. Some of them attach the security object to the app with

app.security = Security(app, user_datastore)

and later uses

app.security.datastore.create_user(email="[email protected]"...

whereas in other places, I see

security = Security(app, user_datastore)
user_datastore.create_user(email="[email protected]"...

I'm trying to get around it by doing something like this

database.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from root.config import config


def get_engine():
    return create_engine(config.get('db').get('DATABASE_URI'), echo=False)


# Use this session for everything other than flask-security
def get_session():
    engine = get_engine()
    return sessionmaker(bind=engine)()

app.py

import os

from flask import Flask
from flask_security import SQLAlchemySessionUserDatastore, Security, hash_password
from sqlalchemy.orm import scoped_session, sessionmaker

from root.db.ModelBase import ModelBase
from root.db.database import get_engine
from root.db.models import User, Role
from root.mail import mail_init
from root.views.calibration_views import calibration

nwm_app = Flask(__name__)

nwm_app.config["SECURITY_REGISTERABLE"] = True

# Use this session for flask-security
session = scoped_session(sessionmaker(bind=get_engine()))

# This is global.  Is it going to affect other parts of my application if I'm using SQLAlchemy 'select'?
ModelBase.query = session.query_property()
user_datastore = SQLAlchemySessionUserDatastore(session, User, Role)
security = Security(nwm_app, user_datastore)

# Register blueprints or views here

nwm_app.register_blueprint(calibration)


# one time setup
with nwm_app.app_context():
    # Create a user and role to test with
    # nwm_app.security.datastore.find_or_create_role(
    user_datastore.find_or_create_role(
        # name="user", permissions={"user-read", "user-write"}
        name="user"
    )
    print('created role')
    session.commit()
    # if not nwm_app.security.datastore.find_user(email="[email protected]"):
    if not user_datastore.find_user(email="[email protected]"):
        print('user not found')
        # nwm_app.security.datastore.create_user(email="[email protected]",
        user_datastore.create_user(email="[email protected]",
                                   password=hash_password("password"), roles=["user"])
    session.commit()

if __name__ == '__main__':
    nwm_app.run()


Solution

  • Not a actionable answer - but there is a lot here. First - Flask-Security (FS) supports a few different datastores - but raw SQLAlchemy isn't one of them - close - but currently FS requires a managed session which Flask-SQLAlchemy and the scoped_session provide.

    Second - as I am writing this, I am adding support for Flask-SQLAlchemy-Lite - which has a much thinner footprint than the old Flask-SQLAlchemy - and doesn't use scoped_sessions. Furthermore, as part of this support - I am updating to the 'new' sqlalchemy model of using 'select' rather than 'query' - so that issue will be gone. I believe that with this change - providing your own db.session will work - I'll try to test for that.