1

I am new to using Blueprint & Application-Factory. I am having issues trying to make Flask-Security work with Application factory method. I have gone through several posts and not able to find a solution that works. With my current setup I am getting this error: RecursionError: maximum recursion depth exceeded while calling a Python object and I believe setting @security.context_processor is causing this. I would like to know if the way I have structured this makes sense and how I can integrate Flask-Security in application factory. Application is structured as below (I have some celery tasks in here but removed it for now for simplicity):

app_factory
- app/
  - __init__.py
  - factory.py
  - models.py
  - tasks.py
  - views.py
  - adminbp/
  - securitybp/
  - templates/
    - admin/
    - index.html
  - statics
- config.py
- run.py
app_factory > run.py

from app import factory
import app

if __name__ == "__main__":
    app = factory.create_app()
    app.run()
app_factory > __init_.py
from celery import Celery

def make_celery(app_name=__name__):
    celery_broker_url = ''
    celery_backend_url = ''
    return Celery(app_name, backend=celery_backend_url, broker=celery_broker_url)
celery = make_celery()
app_factory > app > factory.py

import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
from flask_security import Security

db = SQLAlchemy()
admin = Admin()
security = Security()

PKG_NAME = os.path.dirname(os.path.realpath(__file__)).split("\\")[1]
def create_app(app_name=PKG_NAME, **kwargs):
    app = Flask(app_name)
    app.config.from_pyfile('config.py')
    app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+mysqlconnector: ...'
    db.init_app(app)
    admin.init_app(app)

    from app.views import bp
    app.register_blueprint(bp)

    from app.adminbp.routes import adminbp, user_datastore
    app.register_blueprint(adminbp)

    from app.securitybp.routes import securitybp
    app.register_blueprint(securitybp)
    security._state = security.init_app(app, user_datastore)
    # security = Security(app, user_datastore)

    return app
app_factory > app > views.py

import os
import flask_admin
from flask import Blueprint, render_template, current_app
from .factory import db
from .tasks import make_file
from .models import Role, User, MyBaseView


bp = Blueprint("views", __name__, template_folder='templates')

class CustomView(MyBaseView):
    @expose('/')
    def index(self):
        return self.render('admin/custom_index.html')

@bp.route("/")
def index():
    results = "Testing"
    return render_template ('index.html',results=results)
app_factory > app > adminbp > routes.py

from flask import render_template, Blueprint
from ..models import User, Role, MyModelView, UserView, MyBaseView, Role, User
from ..factory import db, admin, security
from flask_admin.contrib.sqla import ModelView

from flask_security import current_user, login_required, RoleMixin, Security, \
SQLAlchemySessionUserDatastore, UserMixin, utils

adminbp = Blueprint('adminbp', __name__,template_folder='templates/admin')

user_datastore = SQLAlchemySessionUserDatastore(db, User, Role)

admin.add_view(MyModelView(Role, db.session, menu_icon_type='fa', menu_icon_value='fa-server', name="Roles"))
admin.add_view(UserView(User, db.session, menu_icon_type='fa', menu_icon_value='fa-users', name="Users"))
app_factory > app > securitybp > routes.py

from flask import render_template, Blueprint
from ..factory import security

securitybp = Blueprint('securitybp', __name__)

# @security.context_processor
def security_context_processor():
    return dict(
        admin_base_template=admin.base_template,
        admin_view=admin.index_view,
        h=admin_helpers,
        get_url=url_for
    )
security.context_processor(security_context_processor)

Thanks in advance for looking into this!

mrzoogle
  • 111
  • 3
  • 14

1 Answers1

0

Looks like you've been trying a few things.

First - initialize security.init_app(app, user_datastore) - don't assign to _state. That's an internally used variable. If you want to track it in you app - assign it to something like app.security or app.mysecurity. Note it is always available as app.extensions["security"] just like any other Flask extension.

Second - not sure what you are trying to accomplish with the securitybp - Flask-Security already has it's own BP - you can change the name as part of initialization.

Finally - I think the real problem is that you have your context_processor as a global - the easiest and best place to put it would be in your create_app() method (and use the decorator).

jwag
  • 662
  • 5
  • 6
  • thanks for your reply, I am not entirely sure how to apply the fixes you've suggested, would it be possible for you to give a code example please? I have updated to ```security.init_app(app, user_datastore)``` Where would I be using this app.extensions["security"]? Regarding second part securitybp, I was just testing out to see how to make flask-security work and I agree, I have removed blueprint from there On context_processor I am not sure how to use the decorator inside create_app() method – mrzoogle Feb 27 '20 at 22:47
  • Basically - just take your context processor code (use the @security.context_processor) and place it in your create_app method. That should resolve your infinite loop. – jwag Feb 28 '20 at 21:11
  • when I try to put this inside create_app() such as this I get recursion error: ``` security.init_app(app, user_datastore) @security.context_processor def security_context_processor(): return dict( admin_base_template=admin.base_template, admin_view=admin.index_view, h=admin_helpers, get_url=url_for )``` but when I leave this inside securitybp > routes.py I get this error: self.db.session.commit() AttributeError: 'SQLAlchemy' object has no attribute 'commit' – mrzoogle Feb 29 '20 at 09:41
  • I obviously haven't downloaded and worked through your code :-) - not quite clear what is going on - there are alot of globals throughout your code so it is hard to follow exact init sequence. Your new error sounds like user_datastore isn't initialized - but you don't say when and where you are getting the error. If you have access to a debugger - I would start setting some breakpoints and looking at the say the security contents... – jwag Feb 29 '20 at 14:30