Flask configuration handling from external file - python

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'])

Related

Flask with Flask-security and Blueprints - Error App-context

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?

Trouble running Flask alongside uWSGI/Nginx

I'm currently facing issues running my Flask app, and I suspect it's related to a recent restructure of my main app files. I'm struggling to identify the source of the problem and would greatly appreciate any assistance.
When I try to run the app using the flask run command, the website is only accessible on the default port 5000. Attempting to access the website at http://127.0.0.1:5000/ results in the error message "This site can't be reached 127.0.0.1 refused to connect."
I've tried changing the port number in the run.py file to a custom value, but despite updating the code, the logs still indicate that the app is using port 5000. Additionally, I've verified that port 5000 isn't being used by any other process.
I have tried disabling the firewall and verifying that there are no other network issues, but the problem still persists.
I'm uncertain whether the issue is with my code or the server configuration, but I suspect it may be related to the recent restructuring of the main app files.
Notably, uWSGI has no problem running on the domain or IP address.
Any insights or solutions to this issue would be greatly appreciated. Thank you for your help.
Below, I have provided the relevant code for context:
Evidenca-2.0/wsgi.ini
[uwsgi]
module = wsgi:app
master = true
processes = 5
socket = wsgi.sock
chmod-socket = 660
vacuum = true
die-on-term = true```
Evidenca-2.0/wsgi.py
from run import app
if __name__ == '__main__':
app.run()
Evidenca-2.0/run.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_minify import Minify
import os
import logging
from apps.config import config_dict
from apps import create_app, db
# Configure the app
app_config_mode = os.getenv('FLASK_ENV', 'development')
try:
app_config = config_dict[app_config_mode.capitalize()]
except KeyError:
logging.warning(f'Invalid config mode: {app_config_mode}. Defaulting to development mode.')
app_config = config_dict['Development']
app = create_app(app_config)
# Initialize extensions
db = SQLAlchemy()
migrate = Migrate(app, db)
minify = Minify(app=app, html=True, js=False, cssless=False)
# Configure logging
log_file_name = 'app.log'
if app_config.DEBUG:
log_level = logging.DEBUG
else:
log_level = logging.INFO
logging.basicConfig(filename=log_file_name, level=log_level)
if app_config.DEBUG:
logging.debug(f'DEBUG = {app_config.DEBUG}')
logging.debug(f'FLASK_ENV = {os.getenv("FLASK_ENV")}')
logging.debug(f'Page Compression = {"FALSE" if app_config.DEBUG else "TRUE"}')
logging.debug(f'DBMS = {app_config.SQLALCHEMY_DATABASE_URI}')
logging.debug(f'ASSETS_ROOT = {app_config.ASSETS_ROOT}')
logging.debug(f'UPLOAD_FOLDER = {app_config.UPLOAD_FOLDER}')
# Start the app
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8000, threaded=True)
Evidenca-2.0/apps/config.py:
import os
from sqlalchemy import create_engine
from flask import Flask
class Config:
basedir = os.path.abspath(os.path.dirname(__file__))
# Set up the App SECRET_KEY
SECRET_KEY = os.getenv('SECRET_KEY', 'S#perS3crEt_007')
# Set up the database configuration
SQLALCHEMY_DATABASE_URI = os.getenv('SQLALCHEMY_DATABASE_URI', 'mysql+pymysql://aviat:admin#localhost/Evidenca')
SQLALCHEMY_TRACK_MODIFICATIONS = False
# Flask-User settings
USER_APP_NAME = "Flask-User QuickStart App" # Shown in and email templates and page footers
USER_ENABLE_EMAIL = False # Disable email authentication
USER_ENABLE_USERNAME = True # Enable username authentication
USER_REQUIRE_RETYPE_PASSWORD = False # Simplify register form
# Assets Management
ASSETS_ROOT = os.getenv('ASSETS_ROOT', '/static/assets')
# File Upload
UPLOAD_FOLDER = os.getenv('UPLOAD_FOLDER', '/uploads')
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
# Set up logging
LOG_FILE_NAME = os.getenv('LOG_FILE_NAME', 'app.log')
LOG_LEVEL = os.getenv('LOG_LEVEL', 'DEBUG')
# Set up the MariaDB database engine
ENGINE = create_engine(SQLALCHEMY_DATABASE_URI, echo=True)
# Set up the app configuration
DEBUG = os.getenv('DEBUG', False)
SESSION_COOKIE_HTTPONLY = True
REMEMBER_COOKIE_HTTPONLY = True
REMEMBER_COOKIE_DURATION = 3600
if DEBUG:
print('DEBUG = ' + str(DEBUG))
print('FLASK_ENV = ' + os.getenv('FLASK_ENV'))
print('Page Compression = FALSE')
print('DBMS = ' + SQLALCHEMY_DATABASE_URI)
print('ASSETS_ROOT = ' + ASSETS_ROOT)
print('UPLOAD_FOLDER = ' + UPLOAD_FOLDER)
class ProductionConfig(Config):
DEBUG = False
class DevelopmentConfig(Config):
DEBUG = False
class DebugConfig(Config):
DEBUG = True
# Load all possible configurations
config_dict = {
'Production': ProductionConfig,
'Development': DevelopmentConfig,
'Debug': DebugConfig
}
Evidenca-2.0/apps/_init_.py:
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2023 - Aviat Networks
This module sets up the main application and initializes its components. It also
registers the necessary extensions and blueprints for the application.
"""
from flask import Flask
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
from importlib import import_module
# Initialize flask app, SQLAlchemy database, and LoginManager
app = Flask(__name__)
db = SQLAlchemy()
login_manager = LoginManager()
def register_extensions(app):
"""
Register the necessary extensions for the application.
"""
db.init_app(app)
login_manager.init_app(app)
def register_blueprints(app):
"""
Register the blueprints for the application.
"""
for module_name in ('authentication', 'inventory'):
module = import_module('apps.{}.routes'.format(module_name))
if module_name == 'authentication':
app.register_blueprint(module.auth_bp, url_prefix='/auth')
if module_name == 'inventory':
app.register_blueprint(module.inventory_blueprint)
def configure_database(app):
"""
Configure the database for the application.
"""
# Initialize the database before the first request
#app.before_first_request
def initialize_database():
db.create_all()
# Remove database session after each request
#app.teardown_request
def shutdown_session(exception=None):
db.session.remove()
def create_app(config):
"""
Create and configure the Flask application.
:param config: The configuration object for the application.
:return: The Flask application instance.
"""
app = Flask(__name__)
app.config.from_object(config)
register_extensions(app)
register_blueprints(app)
configure_database(app)
return app
The odd thing is that it works, if I set it up like this: flask run -h 192.168.X.X How come it doesn't work on localhost?

A secret key is required to use CSRF Linode server, gunicorn, nginx error Flask

I have looked at similar questions to this one and haven't found a solution yet. My flask app (a personal website) runs with full functionality on the local server but when I try to run it on the IP address at port 5000 the contactme page (the only page with forms) returns the runtime error in the title. I have my secret key in the directory /etc/config.json and my config.py, __init__.py, and run.py code is below:
run.py
from personalwebsite import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True)
init.py:
from flask import Flask
from flask_mail import Mail # this package allows us to send an email when the contact form is filled out
from flask_sqlalchemy import SQLAlchemy # Package for our database ORM
from personalwebsite.config import Config
# Instantiate/Create the database
db = SQLAlchemy()
mail = Mail() # Instantiate our Mail object
def create_app(config_class=Config):
app = Flask(__name__) # Instantiate the app
app.config.from_object(Config)
# Imports all routes from the routes.py file
db.init_app(app)
mail.init_app(app)
from personalwebsite.contact.routes import contacts
from personalwebsite.main.routes import main
from personalwebsite.projects.routes import projects
from personalwebsite.resume.routes import resume
app.register_blueprint(contacts)
app.register_blueprint(main)
app.register_blueprint(projects)
app.register_blueprint(resume)
return app
And config.py:
import os
import json
with open('/etc/config.json') as config_file:
config=json.load(config_file)
class Config:
SECRET_KEY = config.get('SECRETKEY')
SQLALCHEMY_DATABASE_URI = "sqlite:///site.db"
MAIL_SERVER = 'smtp.googlemail.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USERNAME = config.get('EMAILUSERNAME')
MAIL_PASSWORD = config.get('EMAILKEY')
I'm not really sure what is wrong here and have been trying to deploy this simple website for the last few days. Any help is appreciated!

Flask - Cannot commit changes on view

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.

Implementing Flask-WhooshAlchemy using an Application Factory

If app is created in global scope
__init__.py
from flask import Flask
app = Flask(__name__)
app.config.from_object('config')
from app import views
This code down below in run.py would be enuf to start using whoosh_search in views.py like post = Post.query.whoosh_search(name, limit=3).all()
run.py
import os
from app import app
from flask.ext.script import Manager
from flask.ext.moment import Moment
from flask.ext.sqlalchemy import SQLAlchemy
import flask.ext.whooshalchemy as whooshalchemy
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] =\
'sqlite:///' + os.path.join(basedir, 'post.sqlite')
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SECRET_KEY'] = 'hard to guess string'
db = SQLAlchemy(app)
manager = Manager(app)
moment = Moment(app)
class Post(db.Model):
__tablename__ = 'post'
__searchable__ = ['body']
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime))
def __repr__(self):
return '<Post %r>' % (self.body)
whooshalchemy.whoosh_index(app, Post)
if __name__ == '__main__':
db.create_all()
manager.run()
So how to implement Flask-WhooshAlchemy using an Application Factory.
__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
bootstrap = Bootstrap()
moment = Moment()
db = SQLAlchemy()
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.init_app(app)
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
How to register app in whooshalchemy.whoosh_index(app, Post) with Application Factory so whoosh_search could be use in views.py like in last example.
In your application factory function fx. after initializing all the apps, and before registering your blueprints. You should import your model and run whoosh_index. Something along the lines of:
from .model import Post
whooshalchemy.whoosh_index(app, Post)

Categories