Reputation: 511
When I try to run my Flask using gunicorn -w 3 wsgi:app
, and visit the page via the ip address, then I receive the following error: RuntimeError: A secret key is required to use CSRF.
in the terminal.
I am not sure what is causing this issue because: I have set a secret key in .env
and I have confirmation that it is being loaded by Flask because when I run the app using flask run --host=0.0.0.0
, then the codeprint(app.config['SECRET_KEY'])
prints the secret key to the terminal.
I imagine this is something to do with Gunicorn needing a different code for it to load the secret key.
My Flask app is structured like so:
-denise
--configmodule.py
--__init__.py
--site.db
--models.py
--main
--static
--template
-migrations
-venv
-wsgi.py
-.env
I don't have much experience setting up servers, here are the code from the relevant files:
__init__.py
file:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from denise.configmodule import Config
from flask_bcrypt import Bcrypt
from flask_login import LoginManager
from flask_mail import Mail
from flask_migrate import Migrate
from flask_wtf.csrf import CSRFProtect
db = SQLAlchemy()
bcrypt = Bcrypt()
login_manager = LoginManager()
mail = Mail()
csrf = CSRFProtect()
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
csrf.init_app(app)
migrate = Migrate(app,db)
db.init_app(app)
bcrypt.init_app(app)
login_manager.init_app(app)
mail.init_app(app)
print('SECRET_KEY')
print(app.config['SECRET_KEY'])
print('SECRET_KEY')
#with app.app_context()
from .main.routes import main
app.register_blueprint(main)
return app
configmodule.py file:
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):
DEBUG=True
SECRET_KEY=environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI='sqlite:///site.db'
ENV='development'
MAIL_SERVER = 'smtp.googlemail.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USERNAME = environ.get('EMAIL_USER')
MAIL_PASSWORD = environ.get('EMAIL_PASS')
MAIL_DEFAULT_SENDER = environ.get('EMAIL_DEFAULT_SENDER')
MAIL_MAX_EMAILS = 5
RECAPTCHA_PUBLIC_KEY = environ.get('RECAPTCHA_PUBLIC')
RECAPTCHA_PRIVATE_KEY = environ.get('RECAPTCHA_PRIVATE')
Wsgi.py file:
from denise import create_app
from dotenv import load_dotenv
load_dotenv('.env')
app = create_app()
if __name__ == "__main__":
app.run()
.env file:
SECRET_KEY='hiiii17011b97b7ed4aeb9ae7f75a0b66a006c8efd4ab0759e5d2'
EMAIL_USER='[email protected]'
EMAIL_DEFAULT_SENDER='[email protected]'
EMAIL_PASS='password'
RECAPTCHA_PUBLIC='randomstringofletter'
RECAPTCHA_PRIVATE='lalalalalal'
I have tried a few tutorials on deploying Flask apps to production by Corey Schafer and Pretty Printed. I guess I have missed something somewhere.
Upvotes: 4
Views: 1627
Reputation: 4401
Good old spamming of print statements to the rescue.
Note, I used gunicorn -w 1 -t 1 wsgi:app
to ensure single worker & thread, as otherwise the console is spammed with print
statements from multple sources.
Importing & loading order is the likely culprit. There's also an issue with the statement load_dotenv(path.join(basedir, '.env'))
in configmodule.py
. it looks for the .env
file in the directory denise
and fails silently.
I've made a minimally reproducable example, see below.
load_dotenv('.env')
in wsgi.py
before importing anything from denise
configmodule.py
to load_dotenv(path.join(basedir, '../.env'))
, but since you use the Application Factory Pattern structure I'm not sure if this is advisable though. This can lead to undesired behavior in the future.from os import environ, path
from dotenv import load_dotenv, dotenv_values
from pprint import pprint
print('--------------------------------------------------------------------------------------------------------------------------------')
print('configmodule')
print('--------------------------------------------------------------------------------------------------------------------------------')
basedir = path.abspath(path.dirname(__file__))
load_dotenv(path.join(basedir, '.env'))
try:
with open(path.join(basedir, '.env')) as infile:
for line in infile:
print(line)
except:
print('=========================== FILENOTFOUND')
class Config(object):
print('--------------------------------------------------------------------------------------------------------------------------------')
print('configmodule:Config')
print('--------------------------------------------------------------------------------------------------------------------------------')
pprint(dotenv_values(path.join(basedir, '.env')))
print('--------------------------------------------------------------------------------------------------------------------------------')
pprint(environ)
DEBUG=True
SECRET_KEY=environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI='sqlite:///site.db'
ENV='development'
MAIL_SERVER = 'smtp.googlemail.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USERNAME = environ.get('EMAIL_USER')
MAIL_PASSWORD = environ.get('EMAIL_PASS')
MAIL_DEFAULT_SENDER = environ.get('EMAIL_DEFAULT_SENDER')
MAIL_MAX_EMAILS = 5
RECAPTCHA_PUBLIC_KEY = environ.get('RECAPTCHA_PUBLIC')
RECAPTCHA_PRIVATE_KEY = environ.get('RECAPTCHA_PRIVATE')
from os import environ
from denise import create_app
from dotenv import load_dotenv, dotenv_values
print('--------------------------------------------------------------------------------------------------------------------------------')
print('WSGI')
print('--------------------------------------------------------------------------------------------------------------------------------')
load_dotenv('.env')
print(dotenv_values('.env'))
print('--------------------------------------------------------------------------------------------------------------------------------')
with open('.env') as infile:
print(infile.readlines())
print('--------------------------------------------------------------------------------------------------------------------------------')
print(environ)
print('--------------------------------------------------------------------------------------------------------------------------------')
app = create_app()
if __name__ == "__main__":
app.run()
I would suggest to modify wsgi.py
to prevent future issues, as stated above, although this might violate PEP8 standards.
from dotenv import load_dotenv
load_dotenv('.env')
from denise import create_app
app = create_app()
if __name__ == "__main__":
app.run()
Upvotes: 3
Reputation: 982
(writing from phone)
Have a look at your wsgi file:
__init__
file__init__
file imports Config, which is not lazily initialized, but on import (i’d change this, but it’s not an issue)__file__
, which is denise
, then you join it with .env
, while there is no env file in denise directoryRegarding running flask run to debug, I don’t use flask, but it may just not use wsgi file.
Upvotes: 1