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

Modellen, klassen, blauwdrukken en sjablonen delen tussen apps met Flask DispatcherMiddleWare

Door het vermijden van duplicatie wordt uw code kleiner en beter te onderhouden.

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

Op deze site draait Flask. Het gebruikt DispatcherMiddleWare om de frontend app en de admin app te draaien. In de Flask documenten staat dat de Flask applicaties in dit geval volledig van elkaar geïsoleerd zijn. Dat is waar, maar vaak is er veel code die we willen delen tussen deze apps.

Dingen die we willen delen

Beide apps gebruiken dezelfde database, wat betekent dat we het models.py bestand willen delen. Dan hebben we bepaalde klassen die we zelf geschreven hebben. Zo heb ik bijvoorbeeld klassen als MailMessage en FormValidation geschreven. Ze moeten door beide apps worden gebruikt.

Ik gebruik ook Blueprints die gedeeld moeten worden, bijvoorbeeld de 'auth' Blueprint die de authenticatie functies afhandelt zoals inloggen, een account aanmaken, wachtwoord resetten. De sjablonen die door deze Blueprints worden gebruikt moeten ook worden gedeeld. Er zijn ook andere sjablonen die gedeeld moeten worden, zoals macro's om formulieren op een pagina te plaatsen, macro's die knoppen in tabellen plaatsen.

Een gedeelde map toevoegen

In een vorig bericht schreef ik over het gebruik van DispatcherMiddleware en presenteerde een basis directory structuur. Nu is het tijd om een gedeelde directory toe te voegen, zie hieronder.

|
|-- 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

Wijzigingen in de statische map

Ik heb al een omgevingsvariabele die de statische directory bevat. De reden hiervoor is dat de statische directory op een andere locatie op het productiesysteem staat. In de create_app() van zowel de app_frontend als de app_admin maken we de Flask app met dezelfde statische map:

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

Wijzigingen in de importen

Natuurlijk moeten we wijzigingen aanbrengen in de invoer. In app_frontend bijvoorbeeld, wijzigen we de import van Python bestanden van:

    # register  blueprints

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

naar:

    # register  blueprints

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

Wijzigingen in template_folders

Dit is een beetje magisch. Ik heb geen template_folder opgegeven bij het maken van de Flask app wat betekent dat Flask de standaard 'templates' map gebruikt. Maar hoe krijgen we toegang tot de gedeelde sjablonen? Gelukkig kunnen we een 'templates' map specificeren wanneer we een Blueprint maken. Als u dat specificeert:

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

Vervolgens vertelt u Flask dat er een directory 'templates' is in de Blueprint 'auth' directory. Deze directory is relatief (!) ten opzichte van de Blueprint 'auth' directory. De directory structuur zou dan zo moeten zijn:

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

Merk op dat er een additonal directory 'auth' in de templates directory zit omdat we het views.py bestand niet willen wijzigen. Dit bestand bevat weergavefuncties die eindigen met:

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

Als we op deze manier te werk zouden gaan krijgen we een sjabloon directory voor elke gedeelde Blueprint. niet echt wat we willen. De gedeelde sjabloon directory moet dezelfde structuur hebben als de app_frontend en app_admin sjabloon directory's, een enkele directory met subdirectories voor elke blauwdruk. Om dit te bereiken wijzigen we de Blauwdruk template_folder om te verwijzen naar shared/templates:

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

We doen dit voor alle gedeelde blauwdrukken en we zijn klaar. Wat magisch is, is dat je dit alleen maar hoeft te doen voor één gedeelde Blauwdruk. Het lijkt erop dat Flask een lijst van sjabloon-zoekmappen aan het bouwen is en zodra we de template_folder voor de 'auth' blauwdruk hebben verwerkt, wordt het pad aan deze lijst toegevoegd en vinden de andere gedeelde blauwdrukken ook hun sjablonen. Uit de Flask documentatie: 'De (Blauwdruk) sjabloonmap is toegevoegd aan het zoekpad van sjablonen, maar met een lagere prioriteit dan de sjabloonmap van de eigenlijke applicatie'.
Dit werkt omdat we in dit geval verwijzen naar een enkele gedeelde sjabloonmap, maar ik zou liever een sjabloon-zoekpadlijst op applicatieniveau willen kunnen specificeren. Er is informatie beschikbaar over hoe u dit kunt doen, zie onderstaande links.

Vertalingen

Wanneer u een meertalige site heeft en Babel gebruikt, moet u ervoor zorgen dat de app_frontend vertalingen niet alleen uit de directory app_frontend worden gegenereerd, maar ook uit de gedeelde directory. Hetzelfde geldt voor app_admin. Om dit te bereiken voegen we de directory toe aan het bestand 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

Samenvatting

Het delen van bestanden tussen Flask DispatcherMiddleWare apps maakt uw werk veel eenvoudiger, zonder doublures. Ik heb een beetje geworsteld met de gedeelde sjablonen. Je moet echt even de tijd nemen om dit te begrijpen. Uiteindelijk heb ik een test gemaakt en de flow in de Jinja code gevolgd.

Het delen van bestanden bleek niet zo moeilijk, het begint met een solide mappenstructuur. Dit bewijst opnieuw de mogelijkheden van Flask. Hacken is niet nodig, het is er allemaal al.

Links / credits

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

Laat een reactie achter

Reageer anoniem of log in om commentaar te geven.

Opmerkingen

Laat een antwoord achter

Antwoord anoniem of log in om te antwoorden.