Manejo de la configuración¶
Las aplicaciones necesitan algún tipo de configuración. Hay diferentes configuraciones que puedes querer cambiar dependiendo del entorno de la aplicación, como activar el modo de depuración, establecer la clave secreta, y otras cosas específicas del entorno.
La forma en que Flask está diseñado normalmente requiere que la configuración esté disponible cuando la aplicación se inicia. Puedes codificar la configuración en el código, lo que para muchas aplicaciones pequeñas no es realmente tan malo, pero hay mejores maneras.
Independientemente de cómo se cargue la configuración, hay un objeto config disponible que contiene los valores de configuración cargados: El atributo config
del objeto Flask
. Este es el lugar donde Flask mismo pone ciertos valores de configuración y también donde las extensiones pueden poner sus valores de configuración. Pero también es donde puedes tener tu propia configuración.
Conceptos básicos de configuración¶
El config
es en realidad una subclase de un diccionario y puede ser modificado como cualquier diccionario:
app = Flask(__name__)
app.config['TESTING'] = True
Ciertos valores de configuración también se envían al objeto Flask
para que puedas leerlos y escribirlos desde allí:
app.testing = True
Para actualizar varias claves a la vez se puede utilizar el método dict.update()
:
app.config.update(
TESTING=True,
SECRET_KEY='192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
)
Debug Mode¶
The DEBUG
config value is special because it may behave inconsistently if
changed after the app has begun setting up. In order to set debug mode reliably, use the
--debug
option on the flask
or flask run
command. flask run
will use the
interactive debugger and reloader by default in debug mode.
$ flask --app hello run --debug
Using the option is recommended. While it is possible to set DEBUG
in your
config or code, this is strongly discouraged. It can’t be read early by the
flask run
command, and some systems or extensions may have already configured
themselves based on a previous value.
Valores de configuración incorporados¶
Los siguientes valores de configuración son utilizados internamente por Flask:
- DEBUG¶
Whether debug mode is enabled. When using
flask run
to start the development server, an interactive debugger will be shown for unhandled exceptions, and the server will be reloaded when code changes. Thedebug
attribute maps to this config key. This is set with theFLASK_DEBUG
environment variable. It may not behave as expected if set in code.No active el modo de depuración cuando se despliegue en producción.
Por defecto:
False
- TESTING¶
Activar el modo de prueba. Las excepciones se propagan en lugar de ser manejadas por los manejadores de errores de la aplicación. Las extensiones también pueden cambiar su comportamiento para facilitar las pruebas. Deberías habilitar esto en tus propias pruebas.
Por defecto:
False
- PROPAGATE_EXCEPTIONS¶
Las excepciones se vuelven a lanzar en lugar de ser manejadas por los manejadores de errores de la aplicación. Si no se establece, esto es implícitamente cierto si
TESTING
oDEBUG
está activado.Por defecto:
None
- TRAP_HTTP_EXCEPTIONS¶
Si no hay un manejador para una excepción de tipo
HTTPException
, vuelve a lanzarla para que sea manejada por el depurador interactivo en lugar de devolverla como una simple respuesta de error.Por defecto:
False
- TRAP_BAD_REQUEST_ERRORS¶
Intentar acceder a una clave que no existe desde los dicts de petición como
args
yform
devolverá una página de error 400 Bad Request. Habilita esto para tratar el error como una excepción no manejada en su lugar para que obtengas el depurador interactivo. Esta es una versión más específica deTRAP_HTTP_EXCEPTIONS
. Si no se activa, se habilita en modo de depuración.Por defecto:
None
- SECRET_KEY¶
Una clave secreta que se utilizará para firmar de forma segura la cookie de sesión y puede ser utilizada para cualquier otra necesidad relacionada con la seguridad por las extensiones o su aplicación. Debe ser un
bytes
ostr
largo y aleatorio. Por ejemplo, copie la salida de esto a su config:$ python -c 'import secrets; print(secrets.token_hex())' '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
No reveles la clave secreta al publicar las preguntas o comprometer el código.
Por defecto:
None
- SESSION_COOKIE_NAME¶
El nombre de la cookie de sesión. Se puede cambiar en caso de que ya tenga una cookie con el mismo nombre.
Por defecto:
'session'
- SESSION_COOKIE_DOMAIN¶
The value of the
Domain
parameter on the session cookie. If not set, browsers will only send the cookie to the exact domain it was set from. Otherwise, they will send it to any subdomain of the given value as well.Not setting this value is more restricted and secure than setting it.
Por defecto:
None
Changelog
Distinto en la versión 2.3: Not set by default, does not fall back to
SERVER_NAME
.
- SESSION_COOKIE_PATH¶
La ruta para la que será válida la cookie de sesión. Si no se establece, la cookie será válida bajo
APPLICATION_ROOT
o/
si no se establece.Por defecto:
None
- SESSION_COOKIE_HTTPONLY¶
Los navegadores no permiten el acceso de JavaScript a las cookies marcadas como «sólo HTTP» por seguridad.
Por defecto:
True
- SESSION_COOKIE_SECURE¶
Los navegadores sólo enviarán las cookies con solicitudes a través de HTTPS si la cookie está marcada como «segura». La aplicación debe ser servida a través de HTTPS para que esto tenga sentido.
Por defecto:
False
- SESSION_COOKIE_SAMESITE¶
Restringe cómo se envían las cookies con las solicitudes de sitios externos. Puede establecerse como
'Lax'
(recomendado) o'Strict'
. Ver Set-Cookie options.Por defecto:
None
Changelog
Nuevo en la versión 1.0.
- PERMANENT_SESSION_LIFETIME¶
Si
session.permanent
es verdadero, la expiración de la cookie se establecerá este número de segundos en el futuro. Puede ser undatetime.timedelta
o unint
.La implementación de cookies por defecto de Flask valida que la firma criptográfica no sea más antigua que este valor.
Por defecto:
timedelta(days=31)
(2678400
seconds)
- SESSION_REFRESH_EACH_REQUEST¶
Controla si la cookie se envía con cada respuesta cuando
session.permanent
es verdadero. Enviar la cookie cada vez (el valor por defecto) puede evitar que la sesión expire de forma más fiable, pero utiliza más ancho de banda. Las sesiones no permanentes no se ven afectadas.Por defecto:
True
- USE_X_SENDFILE¶
Cuando sirvas archivos, establece la cabecera
X-Sendfile
en lugar de servir los datos con Flask. Algunos servidores web, como Apache, reconocen esto y sirven los datos de manera más eficiente. Esto sólo tiene sentido cuando se utiliza un servidor de este tipo.Por defecto:
False
- SEND_FILE_MAX_AGE_DEFAULT¶
Cuando se sirven archivos, establece la edad máxima del control de la caché a este número de segundos. Puede ser un
datetime.timedelta
o unint
. Anula este valor en base a cada archivo usandoget_send_file_max_age()
en la aplicación o blueprint.Si es
None
,send_file
indica al navegador que se utilizarán peticiones condicionales en lugar de una caché temporizada, lo que suele ser preferible.Por defecto:
None
- SERVER_NAME¶
Informa a la aplicación de a qué host y puerto está vinculada. Necesario para el soporte de coincidencia de rutas de subdominio.
Si se establece,
url_for
puede generar URLs externas con sólo un contexto de aplicación en lugar de un contexto de solicitud.Por defecto:
None
Changelog
Distinto en la versión 2.3: Does not affect
SESSION_COOKIE_DOMAIN
.
- APPLICATION_ROOT¶
Informa a la aplicación de la ruta en la que está montada por la aplicación/servidor web. Esto se utiliza para generar URLs fuera del contexto de una solicitud (dentro de una solicitud, el despachador es responsable de establecer
SCRIPT_NAME
en su lugar; ver Despacho de aplicaciones para ejemplos de configuración de despachos)Se utilizará para la ruta de la cookie de sesión si
SESSION_COOKIE_PATH
no se establece.Por defecto:
'/'
- PREFERRED_URL_SCHEME¶
Utilice este esquema para generar URLs externas cuando no esté en un contexto de solicitud.
Por defecto:
'http'
- MAX_CONTENT_LENGTH¶
No leer más de esta cantidad de bytes de los datos de la solicitud entrante. Si no se establece y la solicitud no especifica un
CONTENT_LENGTH
, no se leerá ningún dato por seguridad.Por defecto:
None
- TEMPLATES_AUTO_RELOAD¶
Recarga las plantillas cuando se modifican. Si no se establece, se habilitará en modo de depuración.
Por defecto:
None
- EXPLAIN_TEMPLATE_LOADING¶
Registra la información de depuración que rastrea cómo se cargó un archivo de plantilla. Esto puede ser útil para averiguar por qué no se ha cargado una plantilla o parece que se ha cargado un archivo incorrecto.
Por defecto:
False
- MAX_COOKIE_SIZE¶
Avisa si las cabeceras de las cookies son mayores que esta cantidad de bytes. Por defectos a
4093
. Las cookies más grandes pueden ser ignoradas silenciosamente por los navegadores. Establezca0
para desactivar la advertencia.
Changelog
Distinto en la versión 2.3: JSON_AS_ASCII
, JSON_SORT_KEYS
, JSONIFY_MIMETYPE
, and
JSONIFY_PRETTYPRINT_REGULAR
were removed. The default app.json
provider has
equivalent attributes instead.
Distinto en la versión 2.3: ENV
was removed.
Distinto en la versión 2.2: Se ha eliminado PRESERVE_CONTEXT_ON_EXCEPTION
.
Distinto en la versión 1.0: Se han eliminado LOGGER_NAME
y LOGGER_HANDLER_POLICY
. Consulte Registro para obtener información sobre la configuración.
Se ha añadido ENV
para reflejar la variable de entorno FLASK_ENV
.
Añadido SESSION_COOKIE_SAMESITE
para controlar la opción SameSite
de la cookie de sesión.
Añadido MAX_COOKIE_SIZE
para controlar un aviso de Werkzeug.
Nuevo en la versión 0.11: SESSION_REFRESH_EACH_REQUEST
, TEMPLATES_AUTO_RELOAD
,
LOGGER_HANDLER_POLICY
, EXPLAIN_TEMPLATE_LOADING
Nuevo en la versión 0.10: JSON_AS_ASCII
, JSON_SORT_KEYS
, JSONIFY_PRETTYPRINT_REGULAR
Nuevo en la versión 0.9: PREFERRED_URL_SCHEME
Nuevo en la versión 0.8: TRAP_BAD_REQUEST_ERRORS
, TRAP_HTTP_EXCEPTIONS
,
APPLICATION_ROOT
, SESSION_COOKIE_DOMAIN
,
SESSION_COOKIE_PATH
, SESSION_COOKIE_HTTPONLY
,
SESSION_COOKIE_SECURE
Nuevo en la versión 0.7: PROPAGATE_EXCEPTIONS
, PRESERVE_CONTEXT_ON_EXCEPTION
Nuevo en la versión 0.6: MAX_CONTENT_LENGTH
Nuevo en la versión 0.5: SERVER_NAME
Nuevo en la versión 0.4: LOGGER_NAME
Configuración desde archivos Python¶
Configuration becomes more useful if you can store it in a separate file, ideally located outside the actual application package. You can deploy your application, then separately configure it for the specific deployment.
A common pattern is this:
app = Flask(__name__)
app.config.from_object('yourapplication.default_settings')
app.config.from_envvar('YOURAPPLICATION_SETTINGS')
Primero se carga la configuración del módulo tuaplicacion.default_settings
y luego se sustituyen los valores por el contenido del archivo al que apunta la variable de entorno TUAPLICACION_SETTINGS
. Esta variable de entorno se puede establecer en el shell antes de iniciar el servidor:
$ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
$ flask run
* Running on http://127.0.0.1:5000/
$ set -x YOURAPPLICATION_SETTINGS /path/to/settings.cfg
$ flask run
* Running on http://127.0.0.1:5000/
> set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg
> flask run
* Running on http://127.0.0.1:5000/
> $env:YOURAPPLICATION_SETTINGS = "\path\to\settings.cfg"
> flask run
* Running on http://127.0.0.1:5000/
Los propios archivos de configuración son archivos reales de Python. Sólo los valores en mayúsculas se almacenan en el objeto config más tarde. Así que asegúrese de usar letras mayúsculas para sus claves de configuración.
Aquí hay un ejemplo de un archivo de configuración:
# Example configuration
SECRET_KEY = '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
Asegúrese de cargar la configuración muy pronto, para que las extensiones tengan la capacidad de acceder a la configuración cuando se inicie. También hay otros métodos en el objeto config para cargar desde archivos individuales. Para una referencia completa, lea la documentación del objeto Config
.
Configuración a partir de archivos de datos¶
También es posible cargar la configuración desde un archivo en un formato de su elección utilizando from_file()
. Por ejemplo, para cargar desde un archivo TOML:
import tomllib
app.config.from_file("config.toml", load=tomllib.load, text=False)
O desde un archivo JSON:
import json
app.config.from_file("config.json", load=json.load)
Configuración desde las variables de entorno¶
Además de apuntar a los archivos de configuración utilizando variables de entorno, puede resultar útil (o necesario) controlar sus valores de configuración directamente desde el entorno. Se puede instruir a Flask para que cargue todas las variables de entorno que comiencen con un prefijo específico en la configuración utilizando from_prefixed_env()
.
Las variables de entorno pueden establecerse en el shell antes de iniciar el servidor:
$ export FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"
$ export FLASK_MAIL_ENABLED=false
$ flask run
* Running on http://127.0.0.1:5000/
$ set -x FLASK_SECRET_KEY "5f352379324c22463451387a0aec5d2f"
$ set -x FLASK_MAIL_ENABLED false
$ flask run
* Running on http://127.0.0.1:5000/
> set FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"
> set FLASK_MAIL_ENABLED=false
> flask run
* Running on http://127.0.0.1:5000/
> $env:FLASK_SECRET_KEY = "5f352379324c22463451387a0aec5d2f"
> $env:FLASK_MAIL_ENABLED = "false"
> flask run
* Running on http://127.0.0.1:5000/
Las variables se pueden cargar y acceder a través de la configuración con una clave igual al nombre de la variable de entorno sin el prefijo, por ejemplo:
app.config.from_prefixed_env()
app.config["SECRET_KEY"] # Is "5f352379324c22463451387a0aec5d2f"
El prefijo es FLASK_
por defecto. Esto es configurable mediante el argumento prefix
de from_prefixed_env()
.
Los valores serán analizados para intentar convertirlos a un tipo más específico que las cadenas. Por defecto se utiliza json.loads()
, por lo que cualquier valor JSON válido es posible, incluyendo listas y dicts. Esto es configurable mediante el argumento loads
de from_prefixed_env()
.
Cuando se añade un valor booleano con el análisis JSON por defecto, sólo «true» y «false», en minúsculas, son valores válidos. Ten en cuenta que cualquier cadena no vacía es considerada True
por Python.
Es posible establecer claves en diccionarios anidados separando las claves con doble guión bajo (__
). Las claves intermedias que no existan en el dictado padre se inicializarán con un dict vacío.
$ export FLASK_MYAPI__credentials__username=user123
app.config["MYAPI"]["credentials"]["username"] # Is "user123"
En Windows, las claves de las variables de entorno son siempre mayúsculas, por lo que el ejemplo anterior terminaría como MYAPI__CREDENTIALS__USERNAME
.
Para obtener aún más características de carga de configuraciones, incluyendo la fusión y el soporte de Windows insensible a las mayúsculas y minúsculas, pruebe una biblioteca dedicada como Dynaconf, que incluye la integración con Flask.
Mejores prácticas de configuración¶
El inconveniente del enfoque mencionado anteriormente es que hace que las pruebas sean un poco más difíciles. No hay una solución única al 100% para este problema en general, pero hay un par de cosas que puedes tener en cuenta para mejorar esa experiencia:
Cree su aplicación en una función y registre los blueprints en ella. De esta manera puedes crear múltiples instancias de tu aplicación con diferentes configuraciones adjuntas lo que hace que las pruebas unitarias sean mucho más fáciles. Puedes usar esto para pasar la configuración según sea necesario.
No escriba código que necesite la configuración en el momento de la importación. Si te limitas a los accesos de sólo petición a la configuración, puedes reconfigurar el objeto más tarde según sea necesario.
Asegúrate de cargar la configuración muy pronto, para que las extensiones puedan acceder a la configuración cuando llamen a
init_app
.
Desarrollo / Producción¶
La mayoría de las aplicaciones necesitan más de una configuración. Debería haber al menos configuraciones separadas para el servidor de producción y el utilizado durante el desarrollo. La forma más fácil de manejar esto es utilizar una configuración por defecto que siempre se carga y forma parte del control de versiones, y una configuración separada que anula los valores según sea necesario como se menciona en el ejemplo anterior:
app = Flask(__name__)
app.config.from_object('yourapplication.default_settings')
app.config.from_envvar('YOURAPPLICATION_SETTINGS')
Entonces sólo tienes que añadir un archivo config.py
separado y exportar TUAPLICACION_SETTINGS=/ruta/al/config.py
y ya está. Sin embargo, también hay formas alternativas. Por ejemplo, puedes usar importaciones o subclases.
Lo que es muy popular en el mundo de Django es hacer la importación explícita en el archivo de configuración añadiendo from yourapplication.default_settings import *
al principio del archivo y luego anulando los cambios a mano. También puedes inspeccionar una variable de entorno como YOURAPPLICATION_MODE
y establecerla como producción
, desarrollo
, etc. e importar diferentes archivos codificados en base a eso.
Un patrón interesante es también utilizar clases y herencia para la configuración:
class Config(object):
TESTING = False
class ProductionConfig(Config):
DATABASE_URI = 'mysql://user@localhost/foo'
class DevelopmentConfig(Config):
DATABASE_URI = "sqlite:////tmp/foo.db"
class TestingConfig(Config):
DATABASE_URI = 'sqlite:///:memory:'
TESTING = True
Para habilitar una configuración de este tipo sólo tienes que llamar a from_object()
:
app.config.from_object('configmodule.ProductionConfig')
Tenga en cuenta que from_object()
no instanciará el objeto de la clase. Si necesita instanciar la clase, por ejemplo para acceder a una propiedad, debe hacerlo antes de llamar a from_object()
:
from configmodule import ProductionConfig
app.config.from_object(ProductionConfig())
# Alternatively, import via string:
from werkzeug.utils import import_string
cfg = import_string('configmodule.ProductionConfig')()
app.config.from_object(cfg)
La instanciación del objeto de configuración le permite utilizar @property
en sus clases de configuración:
class Config(object):
"""Base config, uses staging database server."""
TESTING = False
DB_SERVER = '192.168.1.56'
@property
def DATABASE_URI(self): # Note: all caps
return f"mysql://user@{self.DB_SERVER}/foo"
class ProductionConfig(Config):
"""Uses production database server."""
DB_SERVER = '192.168.19.32'
class DevelopmentConfig(Config):
DB_SERVER = 'localhost'
class TestingConfig(Config):
DB_SERVER = 'localhost'
DATABASE_URI = 'sqlite:///:memory:'
Hay muchas maneras diferentes y depende de ti cómo quieres gestionar tus archivos de configuración. Sin embargo aquí una lista de buenas recomendaciones:
Mantenga una configuración por defecto en el control de versiones. Rellene la configuración con esta configuración por defecto o impórtela en sus propios archivos de configuración antes de anular los valores.
Utilice una variable de entorno para cambiar entre las configuraciones. Esto se puede hacer desde fuera del intérprete de Python y hace que el desarrollo y el despliegue sean mucho más fáciles porque puedes cambiar rápida y fácilmente entre diferentes configuraciones sin tener que tocar el código en absoluto. Si trabajas a menudo en diferentes proyectos puedes incluso crear tu propio script para el sourcing que active un virtualenv y exporte la configuración de desarrollo por ti.
Use a tool like fabric to push code and configuration separately to the production server(s).
Carpetas de instancias¶
Changelog
Nuevo en la versión 0.8.
Flask 0.8 introduce las carpetas de instancia. Durante mucho tiempo Flask hizo posible referirse a rutas relativas a la carpeta de la aplicación directamente (a través de Flask.root_path
). Así era también como muchos desarrolladores cargaban las configuraciones almacenadas junto a la aplicación. Sin embargo, lamentablemente esto sólo funciona bien si las aplicaciones no son paquetes, en cuyo caso la ruta raíz se refiere al contenido del paquete.
Con Flask 0.8 se introdujo un nuevo atributo: Flask.instance_path
. Hace referencia a un nuevo concepto llamado “carpeta de instancia”. La carpeta de instancia está diseñada para no estar bajo control de versiones y ser específica para el despliegue. Es el lugar perfecto para dejar cosas que cambian en tiempo de ejecución o archivos de configuración.
Puedes proporcionar explícitamente la ruta de la carpeta de instancia cuando creas la aplicación Flask o puedes dejar que Flask autodetecte la carpeta de instancia. Para la configuración explícita utilice el parámetro instance_path
:
app = Flask(__name__, instance_path='/path/to/instance/folder')
Por favor, tenga en cuenta que esta ruta debe ser absoluta cuando se proporciona.
Si no se proporciona el parámetro instance_path
se utilizan las siguientes ubicaciones por defecto:
Módulo desinstalado:
/myapp.py /instance
Paquete desinstalado:
/myapp /__init__.py /instance
Módulo o paquete instalado:
$PREFIX/lib/pythonX.Y/site-packages/myapp $PREFIX/var/myapp-instance
$PREFIX
es el prefijo de tu instalación de Python. Puede ser/usr
o la ruta de tu virtualenv. Puedes imprimir el valor desys.prefix
para ver cuál es el prefijo establecido.
Dado que el objeto config proporcionaba la carga de archivos de configuración a partir de nombres de archivo relativos, hemos hecho posible cambiar la carga a través de los nombres de archivo para que sea relativa a la ruta de la instancia si se desea. El comportamiento de las rutas relativas en los archivos de configuración puede cambiarse entre «relativo a la raíz de la aplicación» (por defecto) y «relativo a la carpeta de la instancia» a través del interruptor instance_relative_config
del constructor de la aplicación:
app = Flask(__name__, instance_relative_config=True)
Aquí hay un ejemplo completo de cómo configurar Flask para precargar la configuración de un módulo y luego anular la configuración de un archivo en la carpeta de instancia si existe:
app = Flask(__name__, instance_relative_config=True)
app.config.from_object('yourapplication.default_settings')
app.config.from_pyfile('application.cfg', silent=True)
La ruta de acceso a la carpeta de instancias se puede encontrar a través de Flask.instance_path
. Flask también proporciona un acceso directo para abrir un archivo de la carpeta de instancia con Flask.open_instance_resource()
.
Ejemplo de uso de ambos:
filename = os.path.join(app.instance_path, 'application.cfg')
with open(filename) as f:
config = f.read()
# or via open_instance_resource:
with app.open_instance_resource('application.cfg') as f:
config = f.read()