I am attempting to use the Flask application factory pattern in order to tidy up my code base.
I am able to get this working in a single file as provided by Microsoft Here .
I understand for sqlalchemy I am creating the DB object in the models.py, importing it into my init file, and then I can import and utilize it in any of my blueprint files.
I am wanting to achieve the same thing with the azure auth functionality. At the moment I get an error because in the azure auth file it doesn't know what "app" is to set it up.
Error:
Traceback (most recent call last):
File "C:\Program Files\Python310\lib\runpy.py", line 196, in _run_module_as_main
return _run_code(code, main_globals, None,
File "C:\Program Files\Python310\lib\runpy.py", line 86, in _run_code
exec(code, run_globals)
File "C:\Users\Ben\PycharmProjects\applicationfactoryeexamplke\venv\lib\site-packages\flask\__main__.py", line 3, in <module>
main()
File "C:\Users\Ben\PycharmProjects\applicationfactoryeexamplke\venv\lib\site-packages\flask\cli.py", line 1047, in main
cli.main()
File "C:\Users\Ben\PycharmProjects\applicationfactoryeexamplke\venv\lib\site-packages\click\core.py", line 1055, in main
rv = self.invoke(ctx)
File "C:\Users\Ben\PycharmProjects\applicationfactoryeexamplke\venv\lib\site-packages\click\core.py", line 1657, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "C:\Users\Ben\PycharmProjects\applicationfactoryeexamplke\venv\lib\site-packages\click\core.py", line 1404, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "C:\Users\Ben\PycharmProjects\applicationfactoryeexamplke\venv\lib\site-packages\click\core.py", line 760, in invoke
return __callback(*args, **kwargs)
File "C:\Users\Ben\PycharmProjects\applicationfactoryeexamplke\venv\lib\site-packages\click\decorators.py", line 84, in new_func
return ctx.invoke(f, obj, *args, **kwargs)
File "C:\Users\Ben\PycharmProjects\applicationfactoryeexamplke\venv\lib\site-packages\click\core.py", line 760, in invoke
return __callback(*args, **kwargs)
File "C:\Users\Ben\PycharmProjects\applicationfactoryeexamplke\venv\lib\site-packages\flask\cli.py", line 911, in run_command
raise e from None
File "C:\Users\Ben\PycharmProjects\applicationfactoryeexamplke\venv\lib\site-packages\flask\cli.py", line 897, in run_command
app = info.load_app()
File "C:\Users\Ben\PycharmProjects\applicationfactoryeexamplke\venv\lib\site-packages\flask\cli.py", line 308, in load_app
app = locate_app(import_name, name)
File "C:\Users\Ben\PycharmProjects\applicationfactoryeexamplke\venv\lib\site-packages\flask\cli.py", line 218, in locate_app
__import__(module_name)
File "C:\Users\Ben\PycharmProjects\applicationfactoryeexamplke\myproject\__init__.py", line 11, in <module>
from myproject.azureauth import ms_identity_web
File "C:\Users\Ben\PycharmProjects\applicationfactoryeexamplke\myproject\azureauth.py", line 9, in <module>
app.register_error_handler(NotAuthenticatedError,lambda err: (redirect(url_for('auth.sign_in', post_sign_in_url=request.url_rule))))
NameError: name 'app' is not defined
The aad.config.json file contains the app secrets needed for Azure authentication to work.
My File Structure is as follows:
Top Folder/
├─ aad.config.json
├─ myproject/
│ ├─ __init__.py
│ ├─ models.py
│ ├─ azureauth.py
In my __init__.py
I have the following:
import os
from flask import Flask
from flask_session import Session
from ms_identity_web import IdentityWebPython
from ms_identity_web.adapters import FlaskContextAdapter
from ms_identity_web.errors import NotAuthenticatedError
from ms_identity_web.configuration import AADConfig
# Import the DB module and all the models
from myproject.models import db
from myproject.azureauth import ms_identity_web
aad_configuration = AADConfig.parse_json('aad.config.json')
AADConfig.sanity_check_configs(aad_configuration)
def create_app(test_config=None):
app = Flask(__name__,instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY="mysecretkey",
SQLALCHEMY_DATABASE_URI='sqlite:///mydb.db',
SESSION_TYPE="filesystem"
)
# Import all of the models from . being myproject. import the actual name as seen below
from . import login
# Setup DB as part of this app
db.init_app(app)
db.app = app
# Register each blueprint so that the routes are accessable in the main application
app.register_blueprint(login.bp)
Session(app)
# return the app
return app
In my Models.py
I have the following:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64))
email = db.Column(db.String(128))
password_enc = db.Column(db.String(128))
confirmed = db.Column(db.Boolean, default=False)
My Login.py
:
from flask import Blueprint
from myproject.models import db,User
bp = Blueprint("user",__name__,url_prefix="/login")
@ms_identity_web.login_required
@bp.route("/")
def hello():
db.create_all()
test = User(username="Ben")
db.session.add(test)
db.session.commit()
return("Hello user")
My azureauth.py
:
from flask_session import Session
from ms_identity_web import IdentityWebPython
from ms_identity_web.adapters import FlaskContextAdapter
from ms_identity_web.errors import NotAuthenticatedError
from ms_identity_web.configuration import AADConfig
app.register_error_handler(NotAuthenticatedError,lambda err: (redirect(url_for('auth.sign_in', post_sign_in_url=request.url_rule))))
aad_configuration = AADConfig.parse_json('aad.config.json')
AADConfig.sanity_check_configs(aad_configuration)
adapter = FlaskContextAdapter(app) # ms identity web for python: instantiate the flask adapter
ms_identity_web = IdentityWebPython(aad_configuration, adapter)
I have tried various things, including putting the contents of the azure auth file in the main init file. This doesn't return any errors, but I cannot then import it in a blueprint to use the decorator of @ms_identity_web.login_required
I am wanting to set it up so I can use this decorator to protect any pages in any blueprint.
I can get this working in a singular python file 100% fine. Struggling to wrap my head around where application factories work differently.
In order to use the @ms_identity_web.login_required
decorator (to protect blueprint endpoints) you need to pass it as an app variable:
For example, in your main app ( init .py):
app.config['ms_identity_web'] = ms_identity_web
And then in the blueprint location (azureauth.py):
app = current_app._get_current_object()
with app.app_context():
ms_identity_web = current_app.config['ms_identity_web']
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.