Modellen, klassen, blauwdrukken en sjablonen delen tussen apps met Flask DispatcherMiddleWare
Door het vermijden van duplicatie wordt uw code kleiner en beter te onderhouden.
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
Lees meer
DispatcherMiddleWare Flask
Recent
- Database UUID primaire sleutels van je webapplicatie verbergen
- Don't Repeat Yourself (DRY) met Jinja2
- SQLAlchemy, PostgreSQL, maximum aantal rijen per user
- Toon de waarden in SQLAlchemy dynamische filters
- Veilige gegevensoverdracht met Public Key versleuteling en pyNaCl
- rqlite: een alternatief voor SQLite met hoge beschikbaarheid en distributed
Meest bekeken
- Met behulp van Python's pyOpenSSL om SSL-certificaten die van een host zijn gedownload te controleren
- Gebruik van UUIDs in plaats van Integer Autoincrement Primary Keys met SQLAlchemy en MariaDb
- Maak verbinding met een dienst op een Docker host vanaf een Docker container
- PyInstaller en Cython gebruiken om een Python executable te maken
- SQLAlchemy: Gebruik van Cascade Deletes om verwante objecten te verwijderen
- Flask RESTful API verzoekparametervalidatie met Marshmallow-schema's