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

Paramètres de l'application Flask modifiés à la volée par un administrateur

La configuration de l'application doit être statique, les paramètres de l'application doivent être dynamiques.

26 juillet 2019
Dans Flask
post main image
Original photo unsplash.com/@ssvveennn

Dans Flask nous avons l'objet config qui peut être utilisé pour spécifier les paramètres de la base de données, les paramètres de l'email, etc. Lorsque nous exécutons un programme Flask, il crée d'abord l'application. Une fois l'application créée, les requêtes suivantes sautent la création de l'application et sont redirigées vers les vues Blueprint.

Au démarrage de Flask, les variables de configuration sont chargées et utilisées. Il est important de comprendre que chaque visiteur a sa propre instance. Une fois que l'application Flask est en cours d'exécution, nous pouvons changer les variables de configuration, mais à quoi cela sert-il ? Si nous changeons une variable de configuration dans un cas, cela n'est pas visible dans les autres cas.

Je crois également que nous devrions faire la distinction entre les variables de configuration comme la base de données, l'email, les variables de configuration de session et les variables de paramètres que nous voulons changer à la volée comme le texte du nom du programme affiché dans les modèles ou un booléen allow/block registrations. Nous devrions être capables de gérer les variables de paramétrage à l'aide d'un administrateur et de déclencher d'autres instances en cours d'exécution pour les utiliser sans recharger ces autres instances.

En bref, pour mettre en œuvre les paramètres de l'application, nous devons faire ce qui suit :

  • Définir les classes de paramétrage de l'application
  • Charger la version actuelle des paramètres de l'application lors de la création de l'application Flask
  • Créer une fonction d'administration pour modifier les paramètres de l'application et créer une nouvelle version des paramètres de l'application.
  • Créer une fonction d'administration pour activer une nouvelle version des paramètres de l'application
  • Déclencher d'autres instances pour utiliser une version nouvellement activée des paramètres de l'application
  • Rendre les paramètres de l'application accessibles dans nos modèles Jinja

Définir les classes de paramétrage de l'application

Une exigence est qu'il doit être possible de créer différentes versions des paramètres de l'application. C'est plus de travail mais cela permet de passer sans problème à une nouvelle version et de revenir à une version précédente. J'utilise un attribut de classe / champ de table de base de données seqno pour identifier une version.

Les paramètres de l'application, Paramètres, sont regroupés en sections, SettingSections, et ont un type_valeur qui indique s'il s'agit d'un entier, d'une chaîne ou d'un booléen. Chaque fois que les paramètres de l'application sont enregistrés, une nouvelle version des paramètres de l'application est créée. Cette version peut être inspectée et si tout va bien, elle peut être activée. J'ai aussi ajouté une variable from_seqno qui indique la version que nous avons modifiée, je l'utilise pour montrer les changements entre les versions.

Enfin, il y a les paramètres de l'application seqno ou version, SettingSeqno, qui contient le numéro de version de la version active des paramètres de l'application. Notez que j'ai laissé de côté tout ce qui a trait à l'arrière, je ne pense pas qu'il ajoute quoi que ce soit, voir aussi un billet précédent.

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)

Charger la version actuelle des paramètres de l'application lors de la création de l'application Flask

J'ai fait cela en créant un objet AppSettings, un peu comme lorsque vous créez une extension Flask. Dans la fonction __init__, les paramètres de l'application sont chargés depuis la base de données. Dans la fonction create_app, je viens d'ajouter la ligne :

app.app_settings = AppSettings(app)

Notez que je préfixe app_settings avec'app.' pour rendre app_settings global. Dans la fonction create_app je peux l'appeler app.app_settings et dans les fonctions blueprint je peux l'appeler current_app.app_settings. Jusqu'à présent, nous avons maintenant des paramètres d'application que nous pouvons utiliser dans notre application, par exemple en appelant :

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

Créer une fonction d'administration pour modifier les paramètres de l'application et créer une nouvelle version des paramètres de l'application.

Il s'agit de concevoir des modèles et des codes. J'ai une page de liste qui contient une liste de toutes les versions et une page de modification qui est utilisée pour modifier les paramètres de l'application. Chaque fois que des modifications sont sauvegardées, elles sont sauvegardées dans une nouvelle version des paramètres de l'application. Cette nouvelle version n'est pas automatiquement activée. La page avec les versions montre également les différences entre les paramètres de configuration de l'application en utilisant la difflib de Python.

Créer une fonction d'administration pour activer une nouvelle version des paramètres de l'application

La page de liste avec les versions possède également un groupe de boutons radio avec un bouton radio pour chaque version, le bouton radio activé est coché. Pour passer à une nouvelle version, il suffit de sélectionner la version et dans le code mettre à jour la valeur de SettingSeqno.active_seqno et recharger les versions des paramètres de l'application dans la base de données. Cela fonctionne bien mais seulement dans l'instance de l'administrateur, les autres instances (visiteurs du site) ne le savent pas.

Déclencher d'autres instances pour utiliser une version nouvellement activée des paramètres de l'application

Parce que nous ne voulons pas recharger l'application Flask pour les visiteurs du site, nous devons chercher un autre moyen. Ce que j'ai fait est d'utiliser le gestionnaire Flask before_request. J'utilise une variable de session'app_settings_current_seqno' et une valeur de base de données'active_seqno' pour vérifier si une nouvelle version des paramètres de l'application doit être chargée.

Malheureusement, nous devons ajouter une requête de base de données dans le gestionnaire before_request de Flask pour obtenir la version actuelle des paramètres de l'application. Ceci pourrait être évité en utilisant un fichier partagé, etc. mais comme à l'avenir je peux aussi mettre d'autres valeurs ici aussi, j'ai décidé d'ajouter un objet/table supplémentaire BeforeRequestData qui contient une copie de la variable active_seqno. Pour faire court, si la variable de session diffère de la valeur de la base de données, les paramètres de l'application sont rechargés à partir de celle-ci. Ensuite, la variable de session est mise à jour à la valeur active_seqno, ce qui évite que cela se produise lors de requêtes ultérieures.

    @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

Rendre les paramètres de l'application accessibles dans nos modèles Jinja

On y est presque. Les variables de configuration du moufle sont passées par défaut par le moufle, ce qui vous permet d'accéder aux variables de configuration dans les modèles. Pour transmettre nos paramètres d'application aux modèles, nous utilisons un processeur de contexte. J'ai déjà un processeur de contexte qui injecte des choses, alors je viens d'ajouter les paramètres de l'application.

J'ai également ajouté la version actuelle des paramètres de l'application pour pouvoir facilement voir sur la page Web quelle version des paramètres de l'application est utilisée :

    @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

Et dans le modèle de base :

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

Résumé

Ce n'était pas une tâche facile à mettre en œuvre, mais cela a permis de mieux comprendre Flask. La création d'une application Flask est différente de l'exécution de requêtes ultérieures. Il est confus que pendant l'initialisation nous devions utiliser l'objet Flask app et ensuite l'objet Flask current_app. J'ai lu à ce sujet quelque part, il faudra que je le lise encore, encore et encore, encore et encore.

Liens / crédits

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

Laissez un commentaire

Commentez anonymement ou connectez-vous pour commenter.

Commentaires

Laissez une réponse

Répondez de manière anonyme ou connectez-vous pour répondre.