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

Partage de modèles, de classes, de Blueprints et de modèles entre applications avec Flask DispatcherMiddleWare

En évitant la duplication, votre code devient plus petit et plus facile à maintenir.

12 mai 2020
post main image
https://unsplash.com/@michel_catalisano

Ce site fonctionne sous Flask. Il utilise DispatcherMiddleWare pour exécuter l'application frontale et l'application d'administration. Les documents Flask indiquent que les applications Flask sont, dans ce cas, entièrement isolées les unes des autres. C'est vrai, mais il y a souvent beaucoup de code que nous voulons partager entre ces applications.

Ce que nous voulons partager

Les deux applications utilisent la même base de données, ce qui signifie que nous voulons partager le fichier models.py. Ensuite, nous avons certaines classes que nous avons écrites nous-mêmes. Par exemple, j'ai écrit des classes comme MailMessage et FormValidation. Elles doivent être utilisées par les deux applications.

J'utilise également Blueprints qui devrait être partagé, par exemple l'"auth" Blueprint qui gère les fonctions d'authentification comme la connexion, la création de compte, la réinitialisation du mot de passe. Les modèles utilisés par ces Blueprints doivent également être partagés. Il existe également d'autres modèles qui doivent être partagés, comme les macros permettant de mettre des formulaires sur une page, les macros permettant de mettre des boutons dans des tableaux.

Ajout d'un répertoire partagé

Dans un article précédent, j'ai parlé de l'utilisation de DispatcherMiddleware et j'ai présenté une structure de répertoire de base. Il est maintenant temps d'ajouter un répertoire partagé, voir ci-dessous.

|
|-- project
|   |-- alembic
|   |   `-- 
|   |   
|   |--  app_admin
|   |   |-- __init__.py
|   |   |--  blueprints
|   |   |   |-- content_item
|   |   |   |   |-- forms.py
|   |   |   |   |-- __init__.py
|   |   |   |   `--  views.py
|   |   |   |--  user
|   |   |   |   |-- forms.py
|   |   |   |   |-- __init__.py
|   |   |   |   `--  views.py
|   |   |-- templates
|   |   |   |-- content_item
|   |   |   |   |-- content_items_list.html
|   |   |   |   `-- 
|   |   |   |--  user
|   |   |   |   |--  users_list.html
|   |   |   |   `-- 
|   |   |   |-- base.html
|   |   `-- translations
|   |       `-- es_ES
|   |           `-- LC_MESSAGES
|   |               |-- messages.mo
|   |               `--  messages.po
|   |   
|   |--  app_frontend
|   |   |-- __init__.py
|   |   |--  blueprints
|   |   |   |-- comments
|   |   |   |   |-- forms.py
|   |   |   |   |-- __init__.py
|   |   |   |   `--  views.py
|   |   |   `-- demo_crud_view_uuid
|   |   |       |-- forms.py
|   |   |       |-- __init__.py
|   |   |       `--  views.py
|   |   |-- templates
|   |   |   |-- comments
|   |   |   |   |-- comment_form.html
|   |   |   |   `-- 
|   |   |   `-- base.html
|   |   `-- translations
|   |       `-- es_ES
|   |           `-- LC_MESSAGES
|   |               |-- messages.mo
|   |               `--  messages.po
|   |   
|   |-- shared
|   |   |--  blueprints
|   |   |   |-- account
|   |   |   |   |-- forms.py
|   |   |   |   |-- __init__.py
|   |   |   |   `--  views.py
|   |   |   `-- auth
|   |   |       |-- forms.py
|   |   |       |-- __init__.py
|   |   |       `--  views.py
|   |   |-- static
|   |   |   |-- blog
|   |   |   |-- css
|   |   |   |-- js
|   |   |   |-- vendor
|   |   |   `-- robots.txt
|   |   |-- templates
|   |   |   |-- account
|   |   |   |   `-- overview.html
|   |   |   |-- auth
|   |   |   |   `-- login.html
|   |   |   |-- macros
|   |   |   |   `-- 
|   |   |   `-- boxed.html
|   |   |
|   |   |-- constants.py
|   |   |-- class_app_mail.py
|   |   |-- class_input_validation.py

Modifications du dossier statique

J'ai déjà une variable d'environnement qui contient le répertoire statique. La raison est que le répertoire statique se trouve à un endroit différent sur le système de production. Dans la fonction create_app() des applications app_frontend et app_admin , nous créons l'application Flask avec le même répertoire statique :

    flask_static_folder = os.getenv('FLASK_STATIC_FOLDER')
    app =  Flask(
        __name__, 
        static_folder=flask_static_folder,
    )

Modifications des importations

Bien entendu, nous devons apporter des modifications aux importations initiales. Par exemple, dans les fichiers app_frontend Python , nous modifions les importations des fichiers Python de :

    # register  blueprints

    # authentication (shared)
    from .blueprints.auth.views  import auth_blueprint
    app.register_blueprint(auth_blueprint, url_prefix='/<lang_code>/auth')

à :

    # register  blueprints

    # authentication (shared)
    from shared.blueprints.auth.views  import auth_blueprint
    app.register_blueprint(auth_blueprint, url_prefix='/<lang_code>/auth')

Modifications apportées à template_folders

C'est un peu magique. Je n'ai pas spécifié de template_folder lors de la création de l'application Flask , ce qui signifie que Flask utilise le dossier "templates" par défaut. Mais comment pouvons-nous accéder aux modèles partagés ? Heureusement, nous pouvons spécifier un répertoire "templates" lorsque nous créons une application Blueprint. Si vous le spécifiez :

auth_blueprint  =  Blueprint('auth', __name__,  template_folder='templates')

Si vous ne pouvez pas les utiliser, vous dites à Flask qu'il y a un répertoire "templates" dans le répertoire "auth" de Blueprint . Ce répertoire est relatif ( !) au répertoire "auth" Blueprint . La structure du répertoire doit donc être la suivante :

|   |-- shared
|   |   |--  blueprints
|   |   |   `-- auth
|   |   |       |-- forms.py
|   |   |       |-- __init__.py
|   |   |       |--  views.py
|   |   |       `-- templates
|   |   |           `-- auth
|   |   |               |-- login.html
|   |   |               `-- 

Notez qu'il y a un répertoire supplémentaire "auth" dans le répertoire des modèles car nous ne voulons pas modifier le fichier views.py. Ce fichier contient des fonctions d'affichage se terminant par :

    ...
    return  render_template(
        'auth/login.html',
        ...

Si nous procédons de cette manière, nous obtenons un répertoire modèle pour chaque Blueprint partagé, ce qui n'est pas vraiment ce que nous voulons. Le répertoire de modèles partagé doit avoir la même structure que les répertoires de modèles app_frontend et app_admin , un seul répertoire avec des sous-répertoires pour chaque Blueprint. Pour y parvenir, nous modifions le Blueprint template_folder pour qu'il pointe vers le shared/templates :

auth_blueprint  =  Blueprint('auth', __name__,  template_folder='../../templates')

Nous faisons cela pour tous les Blueprints partagés et nous avons terminé. Ce qui est magique, c'est que vous n'avez à le faire que pour un seul Blueprint partagé. On dirait que Flask est en train de construire une liste de répertoires de recherche de modèles et une fois que nous avons traité la template_folder pour l'"auth" Blueprint , le chemin est ajouté à cette liste et les autres Blueprints partagées trouvent également leurs modèles. Dans la documentation Flask : "Le dossier de modèles (Blueprint) est ajouté au chemin de recherche des modèles, mais avec une priorité inférieure à celle du dossier de modèles de la demande réelle".
Cela fonctionne car dans ce cas, nous nous référons à un seul répertoire de modèles partagés, mais je préférerais pouvoir spécifier une liste de chemins de recherche de modèles au niveau de l'application. Vous trouverez des informations sur la manière de procéder, voir les liens ci-dessous.

Traductions

Lorsque vous avez un site multilingue et que vous utilisez Babel , vous devez vous assurer que les traductions de app_frontend ne sont pas seulement générées à partir du répertoire app_frontend mais aussi à partir du répertoire partagé. Il en va de même pour le répertoire app_admin. Pour ce faire, nous ajoutons le répertoire au fichier babel_app_frontend.cfg :

[python:  app_frontend/**.py]
[python: shared/**.py]
[jinja2:  app_frontend/templates/**.html]
[jinja2:  shared/templates/**.html]
extensions=jinja2.ext.autoescape,jinja2.ext.with_
encoding = utf-8

Résumé

Le partage de fichiers entre les applications Flask DispatcherMiddleWare rend votre travail beaucoup plus facile, sans doublons. J'ai eu quelques difficultés avec les modèles partagés. Vous devez vraiment prendre un certain temps pour comprendre cela. Finalement, j'ai créé un test et j'ai suivi le flux dans le code Jinja .

Le partage de fichiers n'a pas semblé si difficile, il commence par une solide structure de répertoires. Cela prouve une fois de plus les capacités de Flask. Aucun piratage n'est nécessaire, tout est déjà là.

Liens / crédits

Application Dispatching
https://flask.palletsprojects.com/en/1.1.x/patterns/appdispatch/

flask blueprint template folder
https://stackoverflow.com/questions/7974771/flask-blueprint-template-folder

Modular Applications with Blueprints
https://flask.palletsprojects.com/en/1.1.x/blueprints/#modular-applications-with-blueprints

Two Flask apps, frontend and admin, on one domain using DispatcherMiddleware
http://127.0.0.1:8000/en/blog/two-flask-apps-frontend-and-admin-on-one-domain-using-dispatchermiddleware

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.