Flask not reading Secret_key when FLASK_ENV = 'production' - flask

I have a simple Flask app with Config file having Config, Prod and Debug Classes. I have a .env in project root folder, containing the secrets. When I config using Debug it reads the secret key. But when I set it to use Prod it complains:
RuntimeError: The session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret.
I am not using Sessions. Here is the code for Config.py:
from os import environ, path
from dotenv import load_dotenv
basedir = path.abspath(path.dirname(__file__))
load_dotenv(path.join(basedir, '.env'))
class Config:
SECRET_KEY = environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI = environ.get('DB_URI')
class ProdConfig(Config):
FLASK_ENV = 'production'
DEBUG = False
TESTING = False
class DevConfig(Config):
FLASK_ENV = 'development'
DEBUG = True
TESTING = True
Here goes init.py
def create_app(config_class=ProdConfig):
flask_app = Flask(__name__)
flask_app.config.from_object(config_class)
Here is run.py which I use to run the project:
from server import create_app
from server.config import DevConfig
app = create_app()
# app = create_app(DevConfig)
csrf.init_app(app)
if __name__ == '__main__':
app.run()
When using DevConfig it works fine but not with ProdConfig. Thank you

from os import environ, path
from dotenv import load_dotenv
basedir = path.abspath(path.dirname(__file__))
load_dotenv(path.join(basedir, '.env'))
class Config(object):
SECRET_KEY = environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI = environ.get('DB_URI')
class ProdConfig(Config):
SECRET_KEY = environ.get('SECRET_KEY_FOR_PROD')
FLASK_ENV = 'production'
DEBUG = False
TESTING = False
class DevConfig(Config):
FLASK_ENV = 'development'
DEBUG = True
TESTING = True
app.py
def create_app():
flask_app = Flask(__name__)
flask_app.config.from_object(config.ProdConfig)

Related

Flask - How to load database with config from Flask config?

My folder structure is the following:
- app.py
app
- __init__.py
- database.py
in app.py I have:
from app import create_app
app = create_app()
my init.py looks something like:
from flask import Flask
from app.database import db_session, init_db
db = SQLAlchemy()
def create_app():
myapp = Flask(__name__, static_folder='static', static_url_path='/static', template_folder="templates")
myapp.config.from_object('config.Config')
db.init_app(myapp)
migrate.init_app(myapp, db)
# loading blueprints
from app.core_bp import core_bp
myapp.register_blueprint(core_bp, url_prefix='/', template_folder="templates")
return myapp
and database.py looks like this
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:///rapporteur.db')
db_session = scoped_session(sessionmaker(autocommit=False,autoflush=False,bind=engine))
That sqlite path is currently hardcoded to raporteur.db but it should be loaded from config, because I don't want it hardcoded, but unfortunately the flask app is not yet loaded. So how would I do this?
You can use flask-sqlalchemy and set the SQLALCHEMY_DATABASE_URI in the config file.
class Config:
SQLALCHEMY_DATABASE_URI = 'sqlite:///rapporteur.db'
then in your init.py
from youApp import Config
def create_app(config_class=Config)
app = Flask(__name__)
app.config.from_object(config_class)
db = SQLAlchemy()
db.init_app(app)
return app

Correctly Setup Celery with Flask-Application Factory Pattern/Gunicorn/Nginx/Supervisor

I have a task of updating every single row of a MySQL table but it's super slow. I rarely need to do it and only when I change something fundamental, but I thought this would be a great change to learn about multi threading. However all the examples and tutorials online go over some things and not others and I'm struggling to piece all the information together.
I know I need to make a celery process I just don't know if I'm doing it right. A lot of tutorials talk about dockerizing a redis environment without explaining how to do it so I thought I'd come here for some real human-to-human interaction to maybe help me feel less stupid about this.Here's my code so far
/website/__init__.py
from flask import Flask, appcontext_popped, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_migrate import Migrate
from flask_wtf import CSRFProtect
import logging
import celery
#Path Math
import sys
import os
from . import config
db:SQLAlchemy = SQLAlchemy()
migrate = Migrate()
csrf = CSRFProtect()
celery: celery.Celery
DB_NAME = "main"
def create_app(name):
#Flask Instance
app = Flask(__name__)
app.config.from_object(config.ProdTestConfig)
# logging stuff
#Database
db.init_app(app)
migrate.init_app(app, db)
csrf.init_app(app)
global celery
celery = make_celery(app)
with app.app_context():
db.create_all()
# Models and Blueprints here
from .helper_functions import migration_handling as mgh
#where you will find the thing I need to run async
app.before_first_request(mgh.run_back_check)
# log manager stuff
#error page handling
return app
def make_celery(app):
celery = celery.Celery(
app.import_name,
backend=app.config['CELERY_RESULT_BACKEND'],
broker=app.config['CELERY_BROKER_URL']
)
celery.conf.update(app.config)
class ContextTask(celery.Task):
def __call__(self, *args, **kwargs):
with app.app_context():
return self.run(*args, **kwargs)
celery.Task = ContextTask
return celery
I've read some other ways seem to fit a bit better like using:
celery = Celery(__name__, broker=Config.CELERY_BROKER_URL, result_backend=Config.RESULT_BACKEND)
Then in create_app() they run celery.conf.update(app.config). The issue with this is that I don't know how to setup a redis server on my linode machine hosting the site and my personal windows machine. I have redis pip installed. This is how the function I'm trying to run async looks:
#celery.task(name='app.tasks.campaign_pay_out_process')
def campaign_pay_out_process():
'''
Process Every Campaigns Pay
'''
campaign: Campaigns
for campaign in Campaigns.query.filter_by():
campaign.process_pay()
db.session.commit()
current_app.logger.info('Done Campaign Pay Out Processing')
I'm running gunicorn off of supervisor because restarting is super easy and ridding my life of super long linux commands to start a process has been great. I know this is the command for celery: celery -A celery_worker.celery worker --pool=solo --loglevel=info and I'd love to know how to include that in my work flow. Here's my supervisor config:
[program:paymentwebapp]
directory=/home/sai/paymentWebApp
command=/home/sai/paymentWebApp/venv/bin/gunicorn --workers 1 --threads 3 wsgi:app
user=sai
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stderr_logfile=/var/log/paymentwebapp/paymentwebapp.err.log
stdout_logfile=/var/log/paymentwebapp/paymentwebapp.out.log
Here's my flask config right now:
from os import environ, path
from dotenv import load_dotenv
DB_NAME = "main"
class Config:
"""Base config."""
#SESSION_COOKIE_NAME = environ.get('SESSION_COOKIE_NAME')
MAX_CONTENT_LENGTH = 16*1000*1000
RECEIPT_FOLDER = '../uploads/receipts'
IMPORT_FOLDER = 'uploads/imports'
UPLOAD_FOLDER = 'uploads'
EXPORT_FOLDER = '/uploads/exports'
UPLOAD_EXTENSIONS = ['.jpg', '.png', '.pdf', '.csv', '.xls', '.xlsx']
STATIC_FOLDER = 'static'
TEMPLATES_FOLDER = 'templates'
class ProdConfig(Config):
basedir = path.abspath(path.dirname(__file__))
load_dotenv('/home/sai/.env')
env_dict = dict(environ)
FLASK_ENV = 'production'
DEBUG = False
TESTING = False
SQLALCHEMY_DATABASE_URI = environ.get('PROD_DATABASE_URI')
SECRET_KEY = environ.get('SECRET_KEY')
SERVER_NAME = environ.get('SERVER_NAME')
SESSION_COOKIE_SECURE = True
WTF_CSRF_TIME_LIMIT = 600
#Uploads
class DevConfig(Config):
basedir = path.abspath(path.dirname(__file__))
load_dotenv('C:\saiscripts\intercept_branch\Payment Web App Project\.env')
env_dict = dict(environ)
FLASK_ENV = 'development'
DEBUG = True
SQLALCHEMY_DATABASE_URI = environ.get('DEV_DATABASE_URI')
SECRET_KEY = environ.get('SECRET_KEY')
class ProdTestConfig(DevConfig):
'''
Developer config settings but production database server
'''
SQLALCHEMY_DATABASE_URI = environ.get('PROD_DATABASE_URI')
if __name__ == '__main__':
print(environ.get('SQLALCHEMY_DATABASE_URI'))
This is where I copied some code from a tutorial because I'm supposed to make a celery worker:
#!/usr/bin/env python
import os
#from app import create_app, celery
from website import create_app
app = create_app()
app.app_context().push()
from website import celery

config.py did not send back DATABASE_URL

my config.py:
"""Flask config."""
import os
from dotenv import dotenv_values
basedir = os.path.abspath(os.path.dirname(__file__))
configuration = dotenv_values(".env")
class Config(object):
DEBUG = False
TESTING = False
CSRF_ENABLED = True
SECRET_KEY = os.environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
class ProductionConfig(Config):
DEBUG = False
I'm trying to connect to DB with another py file:
import psycopg2
import xlrd
from config import Config
POSTGRES = Config.SQLALCHEMY_DATABASE_URI
connection = psycopg2.connect(POSTGRES)
cursor = connection.cursor()
But POSTRES is None
my .env file is in same directory like this:
FLASK_APP=wsgi.py
FLASK_ENV=development
SECRET_KEY=randomstringofcharacters
DATABASE_URL='postgresql://xxx:xxxxx#127.0.0.1:5432/xxxxx'
All my files are in same directory.
dotenv_values() doesn't side-effect the local environment. You need load_dotenv(), which does.
See https://pypi.org/project/python-dotenv/#load-configuration-without-altering-the-environment

Transferring django settings to Environment Variables

I set the django project settings and added values to environment variables at my local ubuntu mashine and AWS Ubuntu server using .bashrc file at the root folder.
...
export DEBUG="True"
...
settings.py
...
SECRET_KEY = os.environ.get('SECRET_KEY')
DEBUG = os.environ.get('DEBUG', False)
...
At local all working good, but at production server values are not importing. Why doesn't it work on both machines? How do I set up production?
Im running production server using asgi server daphne, accordingly this tutorial
upd
asgi.py
import os
import django
from channels.routing import get_default_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings")
django.setup()
application = get_default_application()
So, what I usually do is: I have a script which runs when Django is setup and creates a .env file with some values.
project_setup.py
import os
import random
import string
def write_dot_env_file(env_file):
settings = get_settings()
with open(env_file, 'w') as f:
for k, v in settings.items():
f.write(f'{k.upper()}={v}\n')
def get_settings():
return {
'SECRET_KEY': generate_secret_key(),
'DEBUG': True,
'ALLOWED_HOSTS': '*',
}
def generate_secret_key():
specials = '!##$%^&*(-_=+)'
chars = string.ascii_lowercase + string.digits + specials
return ''.join(random.choice(chars) for _ in range(50))
def main():
env_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), '.env')
if not os.path.isfile(env_file):
write_dot_env_file(env_file)
else:
pass
if __name__ == '__main__':
main()
Then on my manage.py and wsgi.py just before Django setting the path of the settings you do:
manage.py
#!/usr/bin/env python
import os
import sys
from project_setup import main as project_setup_main
if __name__ == '__main__':
project_setup_main()
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yoursettings')
...
And that's it. When Django runs it'll create a .env file for you with the minimum requirements. Or you can just create the function generate_secret_key inside your settings.py and use it as a default for your SECRET_KEY declaration, like: SECRET_KEY = os.environ.get('SECRET_KEY', get_secret_key())

Flask failed to load configuration from config.py

My __init__.py file
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import app_config
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__, instance_relative_config=True)
app.config.from_object(app_config[config_name])
app.config.from_pyfile('config.py')
db.init_app(app)
#app.route('/')
def index():
return 'Hello, user'
return app
I will find tutorial for flask
build crud app flask
My config.py file:
import os
# Set path for our db
basedir = os.path.dirname(os.path.abspath(__file__))
class Config:
DEBUG = True
class DevConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir,
'bookshell.db')
SQLALCHEMY_ECHO = True
SQLALCHEMY_TRACK_MODIFICATIONS = False
class ProdConfig(Config):
DEBUG = False
class TestConfig(Config):
DEBUG = True
app_config = {
'development': DevConfig,
'production': ProdConfig,
'testing': TestConfig
}
run.py file
import os
# Import from app/__init__
from app import create_app
config_name = os.getenv('FLASK_CONFIG', 'development')
app = create_app(config_name)
if __name__ == '__main__':
app.run()
So, I tried to configure my own config.py file, but I get an error
FileNotFoundError: [Errno 2] Unable to load configuration file (No such file or directory): '/Users/yevhensurzhenko/Desktop/Simple_login_form/instance/config.py'
But when I change some configuration, and when try to reload page it give me
405 error method not allowed
or
app.config.from_object(app_config[config_name])
KeyError: <flask.cli.ScriptInfo object at 0x1038a5b70>
So, I made some changes, like
In config.py add
app_config = {
..
'default': DevConfig
}
In __init__.py create instance dir.
from instance import config
And now I get output from func that I create in __init__.py, in create__app()...
#app.route('/')
def index():
return 'Hello, user'
But, FLASK_CONFIG not change, it work only production, wtf:(