I try to combine flask with flask security and blueprints. But when I try to use the security.context_processor it doesn't work.
app.py
from flask import Flask
from views.default import default
from views.user import user
from views.permission import permission
from playhouse.flask_utils import FlaskDB
from modules.database import *
from flask_security import Security, PeeweeUserDatastore, UserMixin, RoleMixin, auth_required, hash_password
from flask_mailman import Mail
from config import *
import os
import pathlib
app = Flask(__name__)
app = Flask(__name__)
app.register_blueprint(default)
app.register_blueprint(user, url_prefix="/user")
app.register_blueprint(permission, url_prefix="/permission")
app.secret_key = SECRET_KEY
app.config["SESSION_TYPE"] = "filesystem"
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['ALLOWED_IMAGES'] = set(['png', 'jpg', 'jpeg', 'gif'])
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
app.config["SALT"] = ""
# Generate a nice key using secrets.token_urlsafe()
app.config['SECRET_KEY'] = os.environ.get("SECRET_KEY", '')
# Bcrypt is set as default SECURITY_PASSWORD_HASH, which requires a salt
# Generate a good salt using: secrets.SystemRandom().getrandbits(128)
app.config['SECURITY_PASSWORD_SALT'] = os.environ.get("SECURITY_PASSWORD_SALT", '')
app.config['DATABASE'] = {
'name': DB_NAME,
'engine': 'peewee.MySQLDatabase',
}
app.config['MAIL_SERVER'] = MAIL_SERVER
app.config['MAIL_PORT'] = MAIL_PORT
app.config['MAIL_USE_TLS'] = MAIL_TLS
app.config['MAIL_USERNAME'] = MAIL_USERNAME
app.config['MAIL_PASSWORD'] = MAIL_PASSWORD
mail = Mail(app)
# Setup Flask-Security
user_datastore = PeeweeUserDatastore(db, User, Role, UserRoles)
app.security = Security(app, user_datastore)
if __name__ == '__main__':
with app.app_context():
if not app.security.datastore.find_user(email="test#me.com"):
app.security.datastore.create_user(email="test#me.com", password=hash_password("password"))
app.run(host=HOST, port=PORT, threaded=True)
Blueprint file default.py:
from flask import Blueprint, render_template, abort, request, redirect, current_app, flash, session, current_app
from flask_security import Security, PeeweeUserDatastore, UserMixin, RoleMixin, auth_required, hash_password
from config import *
from modules.database import *
from modules.email import *
default = Blueprint('default', __name__, url_prefix="/")
#default.route('/', methods=['GET'])
#current_app.security.context_processor
def index():
return render_template('default.html')
Error message:
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
the current application. To solve this, set up an application context
with app.app_context(). See the documentation for more information.
How can I use the security.context_processor on a blueprint?
Or I have to structure the code to use the app Data in Blueprints. I read the current_app variable is only accessable in a request, but what is the alternative?
Related
I have set all configurations inside Configure class belonging to settings.py module
import os, secrets
basedir = os.path.abspath(os.path.dirname(__file__))
class Config():
SECRET_KEY = secrets.token_hex(16)
SQLALCHEMY_DATABASE_URI ='sqlite:///' + os.path.join(basedir, 'sqlitedb/data.sqlite')
SQLALCHEMY_TRACK_MODIFICATIONS = False
MAIL_SERVER = 'smtp.gmail.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
...........
Which is initialized by main app.py
from flask import Flask
from MyProject.extensions import db, mail, bootstrap, migrate
**from MyProject.settings import Config**
from werkzeug.security import generate_password_hash, check_password_hash
def create_app():
app = Flask(__name__.split('.')[0])
app.url_map.strict_slashes = False
**app.config.from_object(Config)**
register_extensions(app)
register_blueprints(app)
return app
My task is to handover the SECRET_KEY value as an argument to the function which is inside other (mail.py) module
from itsdangerous import URLSafeTimedSerializer
from Myproject.app import mail
from Myproject import app
def send_congrats_email(user):
confirm_serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])
confirm_url = url_for(
'confirm_email',
token=confirm_serializer.dumps(user, salt='email-confirmation-salt'),
_external=True)
send_email('[Congrats] You are registered',
sender= "NICOLAS",
recipients=[user.email],
html_body=render_template('users/email_confirmation.html',
user=user))
Please, advice what is the right/proper way of doing this task?
Solution to such problem is the following:
from flask import current_app
and then modify:
confirm_serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])
to
confirm_serializer = URLSafeTimedSerializer(current_app.config['SECRET_KEY'])
I tried to use Flask-Dance with normal flask app and it works and if I try to implement with flask blueprints it doesn't work. How to register flask-dance to flask blueprints?
My views.py for auth blueprint
from flask import render_template, url_for, redirect, current_app, request
from app.auth import auth
from flask_dance.contrib import github
#auth.route('/login')
def login():
return render_template('auth/login.html')
#auth.route("/")
def github():
if not github.authorized:
return redirect(url_for("github.login"))
resp = github.get("/user")
assert resp.ok
return "You are #{login} on GitHub".format(login=resp.json()["login"])
my init.py for auth blueprint
from flask import Blueprint
from flask_dance.contrib.github import make_github_blueprint, github
auth = Blueprint('auth', __name__, url_prefix='/auth')
blueprint = make_github_blueprint(client_id="m-client-id",client_secret="my-client-secret")
auth.register_blueprint(blueprint, url_prefix="/auth")
from app.auth import views
and my main init.py file:
from flask import Flask
from flask_fontawesome import FontAwesome
from app.config import Config
fa = FontAwesome()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
fa.init_app(app)
from app.public import public
app.register_blueprint(public)
from app.auth import auth
app.register_blueprint(auth)
return app
First you should create and register different blueprint for github.
github/init.py
from flask_dance.contrib import github
from flask_dance.contrib.github import make_github_blueprint
github_blueprint = make_github_blueprint(client_id='your-client-id',client_secret='your-client-secret')
from app.github import views
github/views.py
#github_blueprint.route("/")
def github_login():
if not github.authorized:
return redirect(url_for('github.login'))
account_info = github.get('/user')
if account_info.ok:
account = account_info.json()
return '<h1>Your Github name is {}'.format(account['login'])
and finally in your main init.py file
from flask import Flask
from flask_fontawesome import FontAwesome
from app.config import Config
fa = FontAwesome()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
fa.init_app(app)
from app.public import public
app.register_blueprint(public)
from app.auth import auth
app.register_blueprint(auth)
from app.github import github_blueprint
app.register_blueprint(github_blueprint, url_prefix='/github_login')
#/github_login=callback url
return app
I am getting the following error when trying to set up a scheduled job for my flask app:
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.
I have tried to include the function 'print_session' (which is just a dummy function to check the session data will pull through - in reality this function will query a database) with a 'current_app.appcontext() with loop as I have seen on a few other apps but no joy. Does anyone know why it is still out of the application context?
main.py
from website import create_app
app = create_app()
if __name__=="__main__":
app.run(debug=True,host='localhost',port=5000,threaded=True)
init.py
from flask import Flask, session
from flask_sqlalchemy import SQLAlchemy
from os import path
from flask_session import Session
from flask_login import LoginManager
import redis
db = SQLAlchemy()
DB_NAME = 'sqlite:///db.sqlite3'
sess=Session()
login_manager = LoginManager()
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = "SECRET_KEY"
app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI
app.config['SESSION_TYPE'] = 'SESSION_TYPE'
app.config['SESSION_REDIS'] = 'SESSION_REDIS'
db.init_app(app)
sess.init_app(app)
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
# with app.app_context():
from .views import views
from .auth import auth
app.register_blueprint(views,url_prefix='/')
app.register_blueprint(auth,url_prefix='/')
from .models import User,Token
create_database(app)
return app
def create_database(app):
db.create_all(app=app)
print('Created database')
views.py
from flask import Blueprint,render_template,session,redirect,request,url_for
from flask import current_app
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.schedulers.blocking import BlockingScheduler
import datetime
from flask_login import login_required,current_user
from requests_oauthlib import OAuth2Session
from . import db
from .models import Token
from functools import wraps
def print_session(value):
with current_app.app_context():
print('Yes',value)
return(redirect(url_for('views.home')))
#views.route('/start_schedule')
#login_required
def start_xero_schedule():
with app.app_context():
sched = BackgroundScheduler()
sched.add_job(print_session,'interval',args=[session['value']],seconds=10)
sched.start()
return(redirect(url_for('views.xero')))
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
Below is the python flask code.
All I want to do is split the code into different files as in
all the routes in views.py, configure db in db.py and use them in app.py or where ever required.
Also I want to use db in some of the routes so how do I call it in views.py
from flask import Flask, render_template, request, redirect, make_response, jsonify
from flask_mysqldb import MySQL
from flask_cors import CORS, cross_origin
import yaml
app = Flask(__name__)
# middleware
cors = CORS(app)
# configure db
db = yaml.safe_load(open('db.yaml'))
app.config['MYSQL_HOST'] = db['mysql_host']
app.config['MYSQL_USER'] = db['mysql_user']
app.config['MYSQL_PASSWORD'] = db['mysql_password']
app.config['MYSQL_DB'] = db['mysql_db']
mysql = MySQL(app)
# routes
#app.route("/", methods=['POST'])
def index():
cur = mysql.connection.cursor()
sql_select_query_check = """UPDATE users SET Password = %s, Password_Status = %s WHERE ID = %s"""
cur.execute(sql_select_query_check, (hashcode, 1, userid[0]))
mysql.connection.commit()
return jsonify({"message": "password updated"})
if __name__ == '__main__':
app.run(debug=True, use_reloader=True)
In flask documentation you can see Larger Applications which shows how to split code. And it mentions that main problem can be circular import - main imports views which has to import main to get app. The same problem is with db.py which would need to import main to get app
I move app to separated file application.py to skip circular import
I don't run code and maybe it would need to import mysql to views.py
But maybe it should be done with Blueprint() or with functions which will be imported to main.py and run with argument app.
application.py
from flask import Flask
app = Flask(__name__)
db.py
from application import app
from flask_mysqldb import MySQL
import yaml
db = yaml.safe_load(open('db.yaml'))
app.config['MYSQL_HOST'] = db['mysql_host']
app.config['MYSQL_USER'] = db['mysql_user']
app.config['MYSQL_PASSWORD'] = db['mysql_password']
app.config['MYSQL_DB'] = db['mysql_db']
mysql = MySQL(app)
views.py
from application import app
#app.route("/", methods=['POST'])
def index():
cur = mysql.connection.cursor()
sql_select_query_check = """UPDATE users SET Password = %s, Password_Status = %s WHERE ID = %s"""
cur.execute(sql_select_query_check, (hashcode, 1, userid[0]))
mysql.connection.commit()
return jsonify({"message": "password updated"})
main.py
from application import app
from flask import render_template, request, redirect, make_response, jsonify
from flask_cors import CORS, cross_origin
from views import *
# middleware
cors = CORS(app)
if __name__ == '__main__':
app.run(debug=True, use_reloader=True)
EDIT:
Version with code in functions init() and without application.py - but I didn't test if it will works. But it starts looking like code with Blueprint
db.py
from flask_mysqldb import MySQL
import yaml
def init(app):
db = yaml.safe_load(open('db.yaml'))
app.config['MYSQL_HOST'] = db['mysql_host']
app.config['MYSQL_USER'] = db['mysql_user']
app.config['MYSQL_PASSWORD'] = db['mysql_password']
app.config['MYSQL_DB'] = db['mysql_db']
return MySQL(app)
views.py
def init(app, mysql):
#app.route("/", methods=['POST'])
def index():
cur = mysql.connection.cursor()
sql_select_query_check = """UPDATE users SET Password = %s, Password_Status = %s WHERE ID = %s"""
cur.execute(sql_select_query_check, (hashcode, 1, userid[0]))
mysql.connection.commit()
return jsonify({"message": "password updated"})
main.py
from flask import Flask
from flask import render_template, request, redirect, make_response, jsonify
from flask_cors import CORS, cross_origin
import db
import views
app = Flask(__name__)
# middleware
cors = CORS(app)
myslq = db.init(app)
views.init(app, mysql)
if __name__ == '__main__':
app.run(debug=True, use_reloader=True)
But I would rather keep views with app = Flask(__name__) in one file.
I have this Flask app deployed on a server.
Everything works fine except for the views where I need to db.session.commit() anytihing. I've been looking for circular imports and db engine stuff but I can't seem to understand why it doesn't work.
Below I post my init.py and one of the views I can't seem to make work.
#__init__.py
from flask import Flask
from flask.ext.bootstrap import Bootstrap
from flask.ext.moment import Moment
from flask.ext.sqlalchemy import SQLAlchemy
from config import config
from flask.ext.login import LoginManager
import os
basedir = os.path.abspath(os.path.dirname(__file__))
bootstrap = Bootstrap()
moment = Moment()
db = SQLAlchemy()
login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
bootstrap.init_app(app)
moment.init_app(app)
db.app = app
db.init_app(app)
db.create_all()
login_manager.init_app(app)
#BLUEPRINTS
from main import main as main_blueprint
app.register_blueprint(main_blueprint)
from auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix = '/auth')
from api import api as api_blueprint
app.register_blueprint(api_blueprint, url_prefix = '/api')
return app
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
if __name__ == "__main__":
app.run()
The config.py file where the db settings are defined
# -*- coding: utf-8 -*-
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
SQLALCHEMY_TRACK_MODIFICATIONS = True
# MAIL_SERVER = 'smtp.googlemail.com'
MAIL_SERVER = 'smtp.live.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
# Variáveis de configuração definidas na linha de comandos por motivos de segurança
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
ORGANITE_MAIL_SUBJECT_PREFIX = '[Organite]'
ORGANITE_MAIL_SENDER = '*****#hotmail.com'
ORGANITE_ADMIN = '*****#hotmail.com'
#staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///data-dev.sqlite'
class TestConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data-test.sqlite')
class ProductionConfig(Config):
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
config = {
'development': DevelopmentConfig,
'testing': TestConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
Then on my api blueprint one of the views that don't work:
# -*- coding: utf-8 -*-
from flask import jsonify, request, session, redirect, url_for, current_app
from flask.ext.login import login_user, logout_user, login_required, \
current_user
from .. import db
from ..models import User
from flask.ext.login import login_required
from ..decorators import admin_required, permission_required
from . import api
import cx_Oracle
import datetime
import json
import os
os.environ["NLS_LANG"] = ".UTF8"
#Remover utilizador > JSON
#api.route('/delete/<id>', methods=['GET'])
#login_required
#admin_required
def del_user(id):
user = User.query.filter_by(num_mec=id).first()
if user is not None:
try:
db.session.delete(user)
db.session.commit()
status = 'Sucesso'
except:
status = 'Falhou'
else:
status='Falhou'
db.session.close()
return jsonify({'result': status})
No matter the changes I make the result will always be 'Falhou', meaning the db.session.commit() failed.
I don't even know how to see log errors for this kind of things and I can't seem to understand why it doesn't work.
Please, help, I am running out of time to finish this project.
In this case, unfortunately, the real cause of the error is being obscured by the try: except block which suppresses the error and just handles it by returning the failure result.
Remove the try: except and you will see the cause of the session commit failure in the crash log that Flask generates (in this case, as you mentioned in the comment, there was not sufficient permissions on the database file).
As a general rule, unless an exception is expected and must be handled gracefully, it's better to let it crash and burn with maximum error reporting (at least in debug mode) so that bugs don't slip by silently unnoticed. Since there is no general use-case where you would expect a failed commit to be normal behaviour, you should not be wrapping it in a try: except block.