changed how the Flask app is configured: moved settings from env files to new module config.py; updated README

This commit is contained in:
eclipse 2025-08-22 00:16:17 +02:00
parent 2d3583bbae
commit f9e8b69a06
5 changed files with 70 additions and 40 deletions

View File

@ -1,13 +1,9 @@
# Non-critical configuration values
# Read automatically if python-dotenv is installed
# Non-critical configuration values for the runtime environment
# File is read automatically if python-dotenv is installed
# set this to your app's name if you want to omit the flask option '--app=<app_name>' on the command line
FLASK_APP = "the_works"
FLASK_ENV = "development"
FLASK_SECRET_KEY = "f8148ee5d95b0a67122b1cab9993f637a6bf29528f584a9f1575af1a55566748"
FLASK_TESTING = False
#FLASK_MAX_CONTENT_LENGTH = 1024 * 1024
FLASK_DEBUG = True
FLASK_SQLALCHEMY_DATABASE_URI = "sqlite:///../the_works.sqlite"
FLASK_SQLALCHEMY_ECHO = False
FLASK_SQLALCHEMY_RECORD_QUERIES = True
# environment to run the app in; possible values are "development", "production", "testing"
FLASK_APP_MODE = "development"

View File

@ -16,27 +16,43 @@ the_works also is
## Configuration
The file `.flaskenv` contains the default configuration. Flask reads the file at startup and adds its key-value-pairs to the runtime environment as environment variables. When the Flask app object is being created in `__init__.py`, all environment variables that start with the prefix "FLASK_" get added to the app configuration.
### From the environment
This is true for any prefixed environment variable, not just the ones from `.flaskenv`. It is therefore possible to set additional config parameters by hand before running the app. Just make sure to prefix the variable name with "FLASK_".
When the_works is started, the app first reads all environment variables prefixed with "FLASK_" (e.g. "FLASK_APP"). Any variables in the file `.flaskenv` will be added to the environment beforehand ("python-dotenv" must be installed). This can be used to determine the mode to run the app in (development, production etc.).
Configuration values from the runtime environment can be overridden by using Flask's `-e` command line switch to pass a second config file to the app. This file gets processed the same way as `.flaskenv`, which means that all its keys must be prefixed with "FLASK_". These vars take precedence over the default configuration.
Note that only those environment variables get added to the_works' configuration that begin with "FLASK_". Ths is true whether they were added on the command line, defined in`.flaskenv`, or even added by using Python's `os.environ`.
Finally, you can override config settings with Python during the Flask app's instantiation through the factory. To do this, simply pass a dictionary with (unprefixed) key-value-pairs to `create_app()` method as named parameter `config`. Settings passed this way take precedence over those from the default configuration and additional config files.
### From the_works/config.py
The main configuration happens inside the file `the_works/config.py`. All static variables defined in the class "Config" will be added as key-value-pairs to the_works' configuration dict.
In addition to "Config", the_works will then read all values from either "DevelopmentConfig", "ProductionConfig", or "TestingConfig". These classes are all subclasses of "Config". the_works determines which subclass to use by reading the Flask configuration setting "APP_MODE". If "APP_MODE" is one of either "development", "production", or "testing", the corresponding subclass will be used. If "APP_MODE" is set to a different value or not at all, "DevelopentConfig" will be used as default.
Note that if a value is defined in both "Config" and one of its subclasses, the value from the subclass will supersede the one from the base class.
### Settings for the_works
The following settings are specific to the_works:
* `APP_MODE = development | production | testing` detemines witch configuration to use in addition to the base config; default is development
### Useful Flask settings
* `APP = <app_name>` set this bevore running flask on the command line if you want to omit `--app the_works`
* either `SQLALCHEMY_DATABASE_URI` or `SQLALCHEMY_DATABASE_URI` must be set or flask-sqlalchemy will throw an error; for URI syntax see the [SQLAlchemy docs](https://docs.sqlalchemy.org/en/20/core/engines.html#database-urls)
* see the list of builtin configuration values in the [Flask docs](https://flask.palletsprojects.com/en/stable/config/#builtin-configuration-values)
## Flask commands
Execute commands with `python -m flask <command>`. You don't need to specify `--app the_works` as long as the environment variable "FLASK_APP" is set to "the_works"; the default configuration file does this.
Execute commands with `python -m flask --app the_works <command>`. You can omit `--app` by setting the environment variable "FLASK_APP" to "the_works" (see [here](#useful-flask-settings))
Available commands:
* `run`: Serve app (don't use for production).
<!--* `init-db`: Create empty SQLite database `works.sqlite` in project root. BE CAREFUL: If a database already exists, it will be deleted with everything in it. // 5/25: ich hab die Fkt. wieder rausgenommen, aber ich könnte sie eigentlich prima wieder einbauen … -->
* `shell`: start a shell within the app context (I can i.e. import specific table models and test ORM data structures)
*
@ -48,15 +64,12 @@ Available commands:
Required pip packages
* flask
* flask-sqlalchemy
* python-dotenv
* flask-sqlalchemy
* Pillow
* pytest
Optional pip packages
* flask-debugtoolbar (optional)
* sqlacodegen (optional; only used from the command line during development)
See also `requirements.txt`.
### CSS and Javascript resources

View File

@ -1,19 +1,16 @@
import os
import pytest
from the_works import create_app
from the_works.database import db as _db
from the_works.models import Genre
TEST_DATABASE_URI = "sqlite:///:memory:"
# set app mode to testing via environment variable
os.environ["FLASK_APP_MODE"] = "testing"
@pytest.fixture()
def _app():
test_config = {
"ENV": "Testing",
"SQLALCHEMY_DATABASE_URI": TEST_DATABASE_URI,
"SECRET_KEY": "This is my very secret key",
"TESTING": True
}
_app = create_app(test_config)
_app = create_app()
# other setup can go here
context = _app.app_context()

View File

@ -1,27 +1,29 @@
from flask import Flask
import dotenv # this import is not strictly necessary but it forces pipreqs-to include dotenv when generating `requirements.txt`
import the_works.config as tw_conf
from the_works.database import init_db
from the_works.models import SIMPLE_MODELS
from the_works.views import home, reihe, titelbild, text, veroeffentlichung, werk, ausgabe
from the_works.views.simple_view import VIEWS, ViewAll, ViewCreate, ViewUpdate, ViewDelete
def create_app(config=None):
def create_app():
app = Flask(__name__)
# read all config values from environment that are prefixed with "FLASK_"
# read config from environment (all values are prefixed with "FLASK_")
app.config.from_prefixed_env()
# some #DEBUG configuration
# toolbar = DebugToolbarExtension(app) #DEBUG
# app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False #DEBUG
# use config from function parameter if present
if config:
app.config.update(config)
# read config from object(s)
app.config.from_object(tw_conf.Config)
if 'APP_MODE' in app.config.keys() and isinstance(env := app.config['APP_MODE'], str) and env.lower() == "production":
app.config.from_object(tw_conf.ProductionConfig)
elif 'APP_MODE' in app.config.keys() and isinstance(env := app.config['APP_MODE'], str) and env.lower() == "testing":
app.config.from_object(tw_conf.TestingConfig)
else:
app.config.from_object(tw_conf.DevelopmentConfig)
# some #DEBUG output
print(f"Current Environment: {app.config['ENV'] if 'ENV' in app.config.keys() else 'ENV is not set'}") #DEBUG
print(f"Current mode: {app.config['APP_MODE'] if 'APP_MODE' in app.config.keys() else 'not set'}") #DEBUG
# initialize database
init_db(app)

22
the_works/config.py Normal file
View File

@ -0,0 +1,22 @@
class Config(object):
MAX_CONTENT_LENGTH = 2 * 1024 * 1024
class DevelopmentConfig(Config):
SQLALCHEMY_DATABASE_URI = "sqlite:///../the_works.sqlite"
SECRET_KEY = "f8148ee5d95b0a67122b1cab9993f637a6bf29528f584a9f1575af1a55566748"
SQLALCHEMY_ECHO = False
SQLALCHEMY_RECORD_QUERIES = True
DEBUG = True
class ProductionConfig(Config):
pass
#SQLALCHEMY_DATABASE_URI =
#SECRET_KEY = "differentsecretkey0123456789"
#SQLALCHEMY_RECORD_QUERIES = False
class TestingConfig(Config):
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
SECRET_KEY = "This is my very secret key"
DEBUG = True
TESTING = True