angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

La configuración de la aplicación Flask es modificada sobre la marcha por un administrador

La configuración de la aplicación debe ser estática, los ajustes de la aplicación deben ser dinámicos

26 julio 2019
En Flask
post main image
Original photo unsplash.com/@ssvveennn

En Flask tenemos el objeto config que puede ser usado para especificar parámetros de base de datos, parámetros de correo electrónico, etc. Cuando ejecutamos un programa Flask primero crea la aplicación. Una vez que la aplicación ha sido creada, las solicitudes subsiguientes omiten la creación de la aplicación y son redirigidas a las vistas de planos.

Cuando se inicia Flask, se cargan y utilizan las variables de configuración. Es importante entender que cada visitante tiene su propia instancia. Una vez que la aplicación Flask está funcionando podemos cambiar las variables de configuración, pero ¿de qué sirve esto? Si cambiamos una variable de configuración en una instancia entonces esto no es visible en otras instancias.

También creo que debemos distinguir entre variables de configuración como base de datos, correo electrónico, variables de configuración de sesión y variables de configuración que queremos cambiar sobre la marcha, como el texto del nombre del programa que aparece en las plantillas o un booleano de registros permitidos/bloqueados. Deberíamos ser capaces de gestionar las variables de configuración utilizando un administrador y activar otras instancias de ejecución para que las usen sin recargar estas otras instancias.

En resumen, para implementar la configuración de la aplicación debemos hacer lo siguiente:

  • Definir las clases de configuración de la aplicación
  • Cargar la versión actual de la configuración de la aplicación cuando se crea la aplicación Flask.
  • Realice una función de administrador para editar los ajustes de la aplicación y crear una nueva versión de los ajustes de la aplicación.
  • Realice una función de administración para activar una nueva versión de la configuración de la aplicación.
  • Activar otras instancias para utilizar una versión recién activada de la configuración de la aplicación.
  • Hacer los ajustes de la aplicación accesibles en nuestras plantillas Jinja

Definir las clases de configuración de la aplicación

Un requisito es que debe ser posible crear diferentes versiones de la configuración de la aplicación. Esto es más trabajo, pero permite cambiar sin problemas a una nueva versión y volver a una versión anterior. Utilizo un campo de atributos de clase/tabla de datos seqno para identificar una versión.

Los ajustes de la aplicación, Configuración, están agrupados en secciones, ConfiguraciónSecciones, y tienen un tipo_valor que indica si es un entero, una cadena o un booleano. Cada vez que se guardan los ajustes de la aplicación, se crea una nueva versión de los ajustes de la aplicación. Esta versión puede ser inspeccionada y si todo está bien puede ser activada. También agregué una variable from_seqno que indica la versión que modificamos, la utilizo para mostrar los cambios entre versiones.

Por último, está la configuración de la aplicación seqno o versión, SettingSeqno, que contiene el número de versión de la versión activa de la configuración de la aplicación. Nótese que dejé fuera cualquier cosa que pueble la parte de atrás, no creo que agregue nada, ver también un post anterior.

class SettingSeqno(Base):

    __tablename__ = 'setting_seqno'

    id = Column(Integer, primary_key=True)
    ...
    # only one can be active at a time
    active_seqno = Column(Integer, server_default='0', index=True)


class Setting(Base):

    __tablename__ = 'setting'

    id = Column(Integer, primary_key=True)
    ...
    # seqno settings is the group of settings at a certain moment
    seqno = Column(Integer, server_default='0', index=True)
    name = Column(String(60), server_default='')
    # from_seqno is the seqno that was the base of this seqno
    from_seqno = Column(Integer, server_default='0', index=True)
    value = Column(String(60), server_default='')
    value_type = Column(Integer, server_default='0')
    setting_section_id = Column(Integer, ForeignKey('setting_section.id'))


class SettingSection(Base):

    __tablename__ = 'setting_section'

    id = Column(Integer, primary_key=True)
    ...
    # seqno settings is the group of settings at a certain moment
    seqno = Column(Integer, server_default='0', index=True)
    name = Column(String(60), server_default='')
    # from_seqno is the seqno that was the base of this seqno
    from_seqno = Column(Integer, server_default='0', index=True)

Cargar la versión actual de la configuración de la aplicación cuando se crea la aplicación Flask.

Hice esto creando un objeto AppSettings, un poco como cuando creas una extensión Flask. En la función __init__ se cargan los ajustes de la aplicación desde la base de datos. En la función create_app acabo de añadir la línea:

app.app_settings = AppSettings(app)

Tenga en cuenta que prefijo app_settings con 'app.' para hacer app_settings global. En la función create_app puedo referirme a ella como app.app_settings y en las funciones de planos puedo referirme a ella como current_app.app_settings. Hasta ahora bien, ahora tenemos la configuración de la aplicación que podemos usar en nuestra aplicación, por ejemplo, llamando:

allow_registration = current_app.app_settings.get_value('SECTION_AUTH', 'ALLOW_REGISTRATION')

Realice una función de administrador para editar los ajustes de la aplicación y crear una nueva versión de los ajustes de la aplicación.

Se trata de diseñar plantillas y codificar. Tengo una página de lista que contiene una lista de todas las versiones y una página de edición que se utiliza para editar la configuración de la aplicación. Cada vez que se guardan los cambios, se guardan en una nueva versión de la configuración de la aplicación. Esta nueva versión no se activa automáticamente. La página con las versiones también muestra las diferencias entre los ajustes de la aplicación utilizando la difflib de Python.

Realice una función de administración para activar una nueva versión de la configuración de la aplicación.

La página de lista con las versiones también tiene un grupo de botones de radio con un botón de radio para cada versión, el activado tiene el botón de radio comprobado. Para pasar a una nueva versión, basta con seleccionar la versión y en el código actualizar el valor de SettingSeqno.active_seqno y volver a cargar las versiones de configuración de la aplicación desde la base de datos. Esto funciona bien, pero sólo en el caso del administrador, las otras instancias (visitantes del sitio web) no son conscientes de ello.

Activar otras instancias para utilizar una versión recién activada de la configuración de la aplicación.

Debido a que no queremos recargar la aplicación Flask para los visitantes del sitio web, debemos buscar otra forma. Lo que hice fue usar el manejador Flask before_request. Utilizo una variable de sesión 'app_settings_current_seqno' y el valor de la base de datos 'active_seqno' para comprobar si se debe cargar una nueva versión de la configuración de la aplicación.

Desafortunadamente debemos añadir una petición de base de datos en el gestor before_request de Flask para obtener la versión actual de la configuración de la aplicación. Esto podría evitarse usando un archivo compartido, etc. pero como en el futuro también podría querer poner algunos otros valores aquí también decidí añadir un objeto/tabla extra BeforeRequestData que contenga una copia de la variable active_seqno. Para resumir, si la variable de sesión difiere del valor de la base de datos, los ajustes de la aplicación se recargan de la base de datos. Después de eso, la variable de sesión se actualiza al valor active_seqno evitando que esto ocurra en peticiones posteriores.

    @app.before_request
    def before_request():
        with app.app_context():
            ...
            # reload application settings if session var not equal to database var
            if session_app_settings_current_seqno != database_app_settings_current_seqno:
                app.app_settings.load_from_database(app)
                session['app_settings_current_seqno'] = database_app_settings_current_seqno

Hacer los ajustes de la aplicación accesibles en nuestras plantillas Jinja

Ya casi llegamos. Las variables de configuración del frasco se pasan por defecto por el frasco, lo que le permite acceder a las variables de configuración en las plantillas. Para pasar los ajustes de nuestra aplicación a las plantillas utilizamos un procesador de contexto. Ya tengo un procesador de contexto inyectando cosas, así que simplemente añadí los ajustes de la aplicación.

También añadí la versión actual de la configuración de la aplicación para poder ver fácilmente en la página web qué versión de la configuración de la aplicación se utiliza:

    @app.context_processor
    def inject_base_template_parameters():

        base_template_parameters = {}

        # app_settings
        base_template_parameters['app_settings'] = app.app_settings.get_setting_section_name2setting_name2settings()

        # app_settings seqno
        app_settings_current_seqno = ''
        if session.get('app_settings_current_seqno'):
            app_settings_current_seqno = str( session.get('app_settings_current_seqno') )
        base_template_parameters['app_settings_current_seqno'] = app_settings_current_seqno
        ...
        return base_template_parameters

Y en la plantilla base:

	<p class="copyright text-muted small">
		{{ app_settings['SECTION_GENERAL']['WEBSITE_COPYRIGHT']['value'] }} [{{ app_settings_current_seqno }}] [72ms]
	</p>

Resumen

No fue una tarea fácil de llevar a cabo, pero permitió una mejor comprensión de Flask. La creación de una aplicación Flask es diferente de la ejecución de solicitudes posteriores. Es confuso que durante la inicialización debemos usar el objeto Flask app y después el objeto Flask current_app. He leído sobre esto en alguna parte, tendré que leer sobre ello una y otra vez, y otra vez, y otra vez.

Enlaces / créditos

Configuration Handling
https://flask.palletsprojects.com/en/1.1.x/config/

Context Processors
https://flask.palletsprojects.com/en/1.1.x/templating/#context-processors

Dynaconf - Easy and Powerful Settings Configuration for Python
https://dynaconf.readthedocs.io/en/latest/index.html

How to reload a configuration file on each request for Flask?
https://stackoverflow.com/questions/39456672/how-to-reload-a-configuration-file-on-each-request-for-flask

Deje un comentario

Comente de forma anónima o inicie sesión para comentar.

Comentarios

Deje una respuesta.

Responda de forma anónima o inicie sesión para responder.