Peter Charland
Peter Charland

Reputation: 419

Why is Flask not configuring certain environment variables from the config class? Need help understanding config process

My app appears to be perfectly capable of configuring based on the .env file, the imported config classes, and defining variables directly. However, FLASK_DEBUG is failing to change depending on how I define the variables.

I should probably note that I'm using Visual Studio Code on windows. I've been told I need to use Linux or really anything but windows, and I intend to, but it's not an option right now so any help understanding how this functions with the system I have and how to navigate this would be much appreciated.

config.py:

import os
basedir = os.path.abspath(os.path.dirname(__file__))

class DevelopmentConfig(object):
    os.environ['SECRET_KEY'] = b'something'
    os.environ['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL') or \
        'sqlite:///' + os.path.join(basedir, 'app.db')
    os.environ['SQLALCHEMY_TRACK_MODIFICATIONS'] = 'False'
    os.environ['FLASK_DEBUG'] = '1'
    os.environ['DEV_DICT'] = 'dev_config_class_environ_dictionary_activated_and_working'

class ProductionConfig(object):
    os.environ['SECRET_KEY'] = os.environ.get('SECRET_KEY')
    os.environ['SQLALCHEMY_DATABASE_URI'] = os.environ.get('PRODUCTION_DATABASE_URI')
    os.environ['SQLALCHEMY_TRACK_MODIFICATION'] = 'False'
    os.environ['FLASK_DEBUG'] = '0'
    os.environ['PROD_DICT'] = 'prod_config_class_environ_dictionary_activated_and_working'

init.py:

from flask import Flask
from config import DevelopmentConfig, ProductionConfig
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from dotenv import load_dotenv, find_dotenv
import os

app = Flask(__name__)
db = SQLAlchemy(app)
migrate = Migrate(app, db)

load_dotenv(find_dotenv())

if os.environ.get('FLASK_ENV') == 'development':
    print("Environment is development")
    app.config.from_object(DevelopmentConfig)
elif os.environ.get('FLASK_ENV') == 'production':
    print("Environment is production")
    app.config.from_object(ProductionConfig)

print(os.environ.get('TEST_DOTENV'))   #This value is stored in .env
print(os.environ.get('DEV_DICT'))      #defined in class DevelopmentConfig as os.environ['DEV_DIVT']
print(os.environ.get('PROD_DICT'))     #same but in the ProductionConfig class
print(os.environ.get('FLASK_ENV'))     #defined differently in both classes and CONFIGS CORRECTLY
print(os.environ.get('FLASK_DEBUG'))   #defined differently in both classes and DOES NOT CONFIG CORRECTLY

.env:

FLASK_ENV=development
FLASK_APP=run.py
SECRET_KEY=b'something'
PRODUCTION_DATABASE_URI='something_else'
TEST_DOTENV=config_from_dotenv_is_working   #prints correctly to command line, as do other variables defined here

When I run the app:

(flaskvenv) PS C:\Users\peter\Desktop\Projects\Social Work Site\sw_app> flask run
 * Serving Flask app "run.py" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Restarting with stat
c:\...__init__.py:814: UserWarning: Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. Defaulting SQLALCHEMY_DATABASE_URI to "sqlite:///:memory:".
  'Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. '
c:\...__init__.py:835: FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead... 

Environment is development                                     #changes correctly if I change it in .env
config_from_dotenv_is_working                                  #further proof .env works fine
dev_config_class_environ_dictionary_activated_and_working      #prints regardless of which class gets called
prod_config_class_environ_dictionary_activated_and_working     #also prints regardless of which class gets called
development                                                    #changes to production properly if I change it in .env
0                                       #stubbornly stays set to 0 regardless of it being set in config

 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Here's the strange part:

When I defined FLASK_DEBUG in the .env file, the command line displays it properly in the automatic output, e.g. as * Debug mode: off or * Debug mode: on depending on if I set it to 0 or 1 respectively.

BUT when I call it with os.environ.get('FLASK_DEBUG'), it displays as 0 regardless of what I do.

Based on this, I have a few questions.

The main one and the essence of the problem is of course:

  1. Why is Flask not configuring FLASK_DEBUG but other variables are configuring fine?

Other questions that I suspect might have some kind of connection and would help me understand this:

  1. What role does the Windows OS play in this problem?
  2. Why are both config classes configuring environment variables if I'm only calling one of them?
  3. Why if the automatic output tells me that Debug mode is on does os.environ.get('FLASK_DEBUG') still return 0?

I can solve this by defining FLASK_ENV in .env, but I want to understand how it works. The discrepancy in how things config makes me feel uneasy with not knowing how or why things function the way they do.

Thanks in advance!

Upvotes: 2

Views: 5018

Answers (1)

You are mixing up the concepts of configuration classes and environment variables.

Explanation of your result

If you define two classes and immediately in those definitions set environment variables, both of them will be run immediately. Don't do this:

# config.py
import os

class DevelopmentConfig(object):
    os.environ['FLASK_DEBUG'] = '1'

class ProductionConfig(object):
    os.environ['FLASK_DEBUG'] = '0'

print('FLASK_DEBUG is', os.environ['FLASK_DEBUG'])

After running this code, both classes will have set the env. variable, and since 0 is the last value to be set, the result will be 0:

$ python config.py
FLASK_DEBUG is 0

This is the reason your FLASK_DEBUG is always 0. All of your code inside DevelopmentConfigand ProductionConfig is being run no matter what is set in your .env file. Your problem is not related to Windows.

Besides the classes, your are also setting environment variables from you .env file, including FLASK_ENV=development. This is a variable recognized by Flask, that will turn on debug mode. This the reason debug mode is on in Flask.

One solution: Use environment specific config classes

Define values in classes:

class DevelopmentConfig(object):
    MY_VARIABLE = 'dev value'
    ...

class ProductionConfig(object):
    MY_VARIABLE = 'prod value'
    ...

Then set the environment in an environment variable. This can be done directly in the OS, or you can use an .env file if you like:

FLASK_ENV=development

On your production server, you would create a different .env file:

FLASK_ENV=production

Then load the relevant class in Flask:

from dotenv import load_dotenv, find_dotenv
from flask import Flask
import os

load_dotenv(find_dotenv())    
app = Flask(__name__)
config = ProductionConfig() if os.environ.get('FLASK_ENV') == 'production' else DevelopmentConfig()
app.config.from_object(config)
print(app.config['MY_VARIABLE'])

You don't even need to set FLASK_DEBUG in this case because Flask will set it automatically based on FLASK_ENV.

Other solutions

You can also ditch the config classes completely and instead import all values from environment variables or from configuration files. Read the Flask config guide for more details.

Upvotes: 3

Related Questions