Flask extensions & its configuration - python

I have some trouble finding a pythonic way to run a Flask app with extensions' config parameters.
Here's my myapp/__init__.py, which initializes the flask app and its extensions:
from flask import Flask
from flask.ext.script import Manager
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.redis import Redis
app = Flask(__name__)
manager = Manager(app)
db = SQLAlchemy(app)
redis = Redis(app)
I decided to store app's configuration in separate files for debugging and deployment and loading them with Flask.config.from_pyfile.
Configuration file has several options, including:
SQLALCHEMY_DATABASE_URI = "sqlite:////tmp/test.db"
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379
REDIS_DB = 0
I use Flask-Script to run the server:
from myapp import app, manager
#manager.command
def debug():
app.config.from_pyfile("debug.cfg")
app.run()
While SQLAlchemy works OK (creating an SQLite database at the /tmp/test.db), I have yet to run Redis instance because it seems to try hard and find REDIS_HOST in app.config before it was even loaded from the config file.
Is there any way to bypass it without having to hardcode the default settings before config was imported from file?

Yes, if you pass in app to the Redis() constructor you need to have the configuration loaded.
Postpone passing in app, call redis.init_app(app) after loading the configuration:
app = Flask(__name__)
manager = Manager(app)
db = SQLAlchemy()
redis = Redis()
def attach_extensions(app):
db.init_app(app)
redis.init_app(app)
then when configuration has loaded:
from myapp import app, manager, attach_extensions
#manager.command
def debug():
app.config.from_pyfile("debug.cfg")
attach_extensions(app)
app.run()

Related

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!

Run flask app with python instead of flask run

I'm trying to run my flask app with "python app.py" instead of "flask run" command.
My goal is to launch the app on cpanel server and just about every tutorial requires the applications to be called using the "python" method.
Here is my folder structure:
project
webapp
init.py
templates
static
auth.py
main.py
app.py <-------------- I want this to be called with python instead of flask run command outside the folder
Here is my init_.py file:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
# init SQLAlchemy so we can use it later in our models
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = '9OLWxND4o83j4iuopO'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
db.init_app(app)
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
from .models import User
#login_manager.user_loader
def load_user(user_id):
# since the user_id is just the primary key of our user table, use it in the query for the user
return User.query.get(int(user_id))
# blueprint for auth routes in our app
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
# blueprint for non-auth parts of app
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
And app.py is:
from webapp import app
I'm a newbie in flask, any help is appreciated
Insert the call to create_app at the end of init.py:
if __name__ == '__main__':
create_app().run(host='0.0.0.0', port=5000, debug=True)
The if statement avoid calling the app many times. It can only be called directly. Flask default host is 127.0.0.1 (localhost). Use 0.0.0.0 at production for better traffic monitoring. Default port is also 5000, so it's up to you to include. For better readability, you should explicit it.
Then call it
python webapp/init.py

The session is unavailable because no secret key was set Flask App factory

I'm having trouble logging in my user when i try to set up an app with app factory. The app configures from a config file and when I use a print statement before create_app() it actually shows me the secret key. However when i run the app and enter the username in the login screen I get a runtime error:
RuntimeError: The session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret.
Could anyone point me in the right direction? I cant seem to get my head around it.
app/__init__.py
from flask import Flask
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
db.init_app(app)
app.config.from_object('config.Config')
with app.app_context():
from .auth import auth_routes
app.register_blueprint(auth_routes.auth_routes)
db.create_all()
return app
.env
SESSION_TYPE = filesystem
SECRET_KEY = verysecretkey
config.py
from os import environ
class Config:
SESSION_TYPE = environ.get('SESSION_TYPE')
SECRET_KEY = environ.get('SECRET_KEY')
Conveying settings from .env into a Flask instance has two requirements: First, python-dotenv needs to be installed. Second, you need to be starting your app using either the flask command or by some path that invokes app.run().
I've found it safer to be explicit in my configs, by doing
import os
from dotenv import load_dotenv
load_dotenv()
class Config:
SECRET_KEY = os.environ.get('APP_SECRET_KEY')
...
This avoids confusion when deploying under uwsgi et al.

How to import the app using flask foundation?

I am using Flask foundation to begin with my new flask app.
The init.py file has a method:
def create_app(object_name, env="prod"):
"""
An flask application factory, as explained here:
http://flask.pocoo.org/docs/patterns/appfactories/
Arguments:
object_name: the python path of the config object,
e.g. sservice.settings.ProdConfig
env: The name of the current environment, e.g. prod or dev
"""
app = Flask(__name__)
app.config.from_object(object_name)
app.config['ENV'] = env
#init the cache
cache.init_app(app)
debug_toolbar.init_app(app)
#init SQLAlchemy
db.init_app(app)
login_manager.init_app(app)
# Import and register the different asset bundles
assets_env.init_app(app)
assets_loader = PythonAssetsLoader(assets)
for name, bundle in assets_loader.load_bundles().iteritems():
assets_env.register(name, bundle)
# register our blueprints
from controllers.main import main
app.register_blueprint(main)
return app
that is imported in manage.py.
But what If I need to use this app variable to access the application configuration in modules within the application? I can't use current_app outside request contexts. Creating one is ugly.
I need to have a app variable in my models.py file:
In models.py
# how to import? Below doesn't work
from appname import app
# access config
print(app.config)
I can't call this method create_app, because it should be only called once to create the application. Not anytime you need to import the app. How should I solve this?
I only want to use create_app() exactly once outside the package to give it the wsgi server, but I don't want to use it within the package.
What is working now for me:
Wherever there is no current_app (outside of request contexts I guess) just use something like this
app = Flask(__name__)
app.config.from_object('appname.settings_file.DevConfig')
print(app.config)

Categories