(inspiré d’un tutoriel vidéo vu sur les teachers du net)
Exemple : gestion des évènements
#! /usr/bin/python
# -*- coding:utf-8 -*-
from flask import Flask, render_template, request, redirect, url_for, session, escape , flash
from datetime import date, datetime
app = Flask(__name__)
app.secret_key = 'la cle en toc(any random string)'
@app.route('/', methods=['GET'])
def home_index():
return redirect(url_for('show_events'))
@app.route('/events', methods=['GET'])
def show_events():
return render_template('events/showEvents.html')
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5001)
Pour connaître les services Web sur une machine lsof et son adresse [ip](:
hostname -i
ip -4 -c addr show
lsof -i :80
Dans le dossier templates créer un dossier events et une vue events/showEvents.html
{% extends 'layout.html' %}
{% block body %}
<h1>Events</h1>
{% endblock %}
$dateAjourdhui
. Calculer cette date dans le contrôleur. Passer cette date à la vue et l’afficher.@app.route('/events', methods=['GET'])
def show_events():
dateAujourdhui= datetime.now()
print dateAujourdhui, type(dateAujourdhui)
return render_template('events/showEvents.html', dateToday=dateAujourdhui)
Pour l’afficher dans la vue
{{ dateToday }}
Différence Jinja et Twig : Jinja fait du casting (transforme les dates en chaînes de caractères)
cependant comme sur symfony, il est possible de faire du casting
dateAujourdhui= datetime.now().strftime('%d-%m-%Y')
le filtre date : {{ dateToday | date('d-m-Y h:m:s e')}}
comme sur twig n’existe pas, lien pour d’autres solutions
@app.route('/events', methods=['GET'])
def show_events():
dateAujourdhui= datetime.now().strftime('%d-%m-%Y')
evenements = [
'Symfony Conference',
'Laravel Conference',
'Django Conference',
'JAVA2EE Conference',
'Rails Conference'
]
return render_template('events/showEvents.html', dateToday=dateAujourdhui, events = evenements)
Dans la vue events/showEvents.html, remplacer le code par le code ci-dessous pour parcourir un tableau
{% extends 'layout.html' %}
{% block body %}
<h1>{{ events|length }} Events</h1>
<ul>
{% for event in events %}
<li>{{ event }}</li>
{% endfor %}
{{ events }} {# equivalent à dump#}
</ul>
{{ dateToday }}
{% endblock %}
Remarque : sur jinja la méthode dump n’existe pas. Il suffit d’afficher l’objet.
evenements = [
{'id' : 1,'nom' : u'Symfony Conference', 'description' : u'présentation de la conférence sur Symfony ', 'date' : datetime(2019,2,20), 'prix' : 10.5},
{'id' : 2,'nom' : u'Laravel Conference', 'description' : u'présentation de la conférence sur Laravel ', 'date' : datetime(2019,3,2), 'prix' : None},
{'id' : 3,'nom' : u'Django Conference', 'description' : u'présentation de la conférence sur Django ', 'date' : datetime(2019,3,25), 'prix' : 20},
{'id' : 4,'nom' : u'JAVA2EE Conference', 'description' : u'présentation de la conférence sur Java J2EE ', 'date' : datetime(2019,4,2), 'prix' : 30},
{'id' : 5,'nom' : u'Rails Conference', 'description' : u'présentation de la conférence sur Ruby on Rails ', 'date' : datetime(2019,4,26), 'prix' : 12},
]
# result.result_str = unicode(my_string_variable, "utf8")
# result.result_str = u"my string"
https://docs.python.org/fr/3/tutorial/datastructures.html#dictionaries
{{ event }}
par {{ event) }}
{{ event.nom }}
{% endfor %}
par :{% else %}
pas d’événement
{% endfor %}
$evenements = NULL;
pour faire un test
@app.route('/events/details', methods=['GET'])
def details_event():
return render_template('events/detailsEvent.html')
{% extends 'base.html.twig' %}
{% block body %}
<h1>détails de l’évènement</h1>
{% endblock %}
<li>{{ event.nom }} <a href="{{ url_for('details_event') }}">détails</a></li>
remplacer url par path , comparer en affichant le contenu de la route
{% extends 'layout.html' %}
{% block body %}
<h1>{{ events|length }} Events</h1>
<ul>
{% for event in events %}
<li>{{ event.nom }} <a href="{{ url_for('details_event', id = event.id) }}">détails</a></li>
{% else %}
pas d’événement
{% endfor %}
{#{ events }#}
</ul>
{{ dateToday }}
{% endblock %}
@app.route('/events/details/<int:id>', methods=['GET'])
def details_event(id):
evenements = [
{'id' : 1,'nom' : u'Symfony Conference', 'description' : u'présentation de la conférence sur Symfony ', 'date' : datetime.date(2019,2,20), 'prix' : '10.5'},
{'id' : 2,'nom' : u'Laravel Conference', 'description' : u'présentation de la conférence sur Laravel ', 'date' : datetime.date(2019,3,2), 'prix' : None},
{'id' : 3,'nom' : u'Django Conference', 'description' : u'présentation de la conférence sur Django ', 'date' : datetime.date(2019,3,25), 'prix' : '20'},
{'id' : 4,'nom' : u'JAVA2EE Conference', 'description' : u'présentation de la conférence sur Java J2EE ', 'date' : datetime.date(2019,4,2), 'prix' : '30'},
{'id' : 5,'nom' : u'Rails Conference', 'description' : u'présentation de la conférence sur Ruby on Rails ', 'date' : datetime.date(2019,4,26), 'prix' : '12'},
]
evenement=evenements[id-1]
print evenement
return render_template('events/detailsEvent.html', event = evenement)
Dans la barre d’outils, la commande dump ajoute un lien sur le contenu (vers la cible)
{% extends 'layout.html' %}
{% block body %}
{{ event.id }} | {{ event.nom }} | {{ event.description }} | {{ event.date }} | {{ event.prix }} €
<br><br>
<a href="/events">retour listes événements</a>
{% endblock %}
<br><br><a href="{{ url_for('show_events') }}">retour listes événements</a>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Sacramento">
<link rel="icon" type="image/x-icon" href="http://www.iut-bm.univ-fcomte.fr/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="{{ url_for('static', filename='mesStyles.css') }}" rel="stylesheet" type="text/css" />
<title>{% block title %}page layout{% endblock %}</title>
</head>
<body>
<br>
{% include "nav.html" %}
{% block body %}
contenu du "body" de layout.html
{% endblock %}
</body>
</html>
<nav>
<h1><a href="{{ url_for('home_index') }}"> Home </a></h1>
<h1><a href="{{ url_for('show_events') }}">liste des événements</a></h1>
<h1><a href="{{ url_for('details_event', id = 1) }}"> Dernier événement</a></h1>
<h1><a href=""> Ajouter un évènement </a></h1>
</nav>
<hr>
REMARQUE : le lien sur “Dernier évènement” est à modifier, utiliser une variable ou une autre solution (à faire à la fin de l’exercice).
mkdir static
touch static/essai.html static/mesStyles.css
La fonction de twig {{ asset("") }}
permet de faire référence à un contenu du dossier public . Voir la documentation de symfony.
Créer un fichier mesStyles.css dans le dossier public
Recopier le contenu CSS de la documentation dans le fichier mesStyles.css
html { font-family: sans-serif; background: #eee; padding: 1rem; }
body { max-width: 960px; margin: 0 auto; background: white; }
h1 { font-family: serif; color: #377ba8; margin: 1rem 0; }
..........
tester l’URL : http://127.0.0.1:5000/static/mesStyles.css
Ajouter dans le fichier templates/layout.html, dans le bloc stylesheets
<link href="{{ url_for('static', filename='mesStyles.css') }}" rel="stylesheet" type="text/css" />
https://symfony.com/doc/current/controller.html#redirecting
session_start();
, puis on utilise des variables n’importe où dans le script PHP avec le tableau $_SESSION['maVariableDeSession']
.token
de nom PHP_SESSION_ID
dans votre navigateur (client) lors de la commande session_start();
pour savoir à quel client est associé le tableau $_SESSION
.Sur le Framework Symfony, il existe une classe qui gère les sessions PHP : http://api.symfony.com/4.0/Symfony/Component/HttpFoundation/Session/SessionInterface.html, c’est cette classe que l’on utilisera.
Sur Flask http://flask.pocoo.org/docs/1.0/quickstart/#sessions
(à chaque nouvelle version de mozilla, ça change)
$evenements
comme attribut public dans la classe#! /usr/bin/python
# -*- coding:utf-8 -*-
from flask import Flask, render_template, request, redirect, url_for, session, escape, flash
from datetime import date, datetime
app = Flask(__name__)
app.secret_key = 'la cle en toc(any random string)'
app.config.from_object(__name__)
@app.before_request
def before_request():
#session.clear()
app.logger.warning('before call')
if 'evenements' in session:
evenements = session['evenements']
else:
evenements = [
{'id' : 1,'nom' : u'Symfony Conference', 'description' : u'présentation de la conférence sur Symfony ', 'date' : datetime(2019,2,20), 'prix' : 10.5},
{'id' : 2,'nom' : u'Laravel Conference', 'description' : u'présentation de la conférence sur Laravel ', 'date' : datetime(2019,3,2), 'prix' : None},
{'id' : 3,'nom' : u'Django Conference', 'description' : u'présentation de la conférence sur Django ', 'date' : datetime(2019,3,25), 'prix' : 20},
{'id' : 4,'nom' : u'JAVA2EE Conference', 'description' : u'présentation de la conférence sur Java J2EE ', 'date' : datetime(2019,4,2), 'prix' : 30},
{'id' : 5,'nom' : u'Rails Conference', 'description' : u'présentation de la conférence sur Ruby on Rails ', 'date' : datetime(2019,4,26), 'prix' : 12},
]
session['evenements']=evenements
$evenements
dans les methodes detailsEvent et showEvents utiliser celui qui définit en session (remplacer l’instruction, recopier le code ci-dessous :@app.route('/events', methods=['GET'])
def show_events():
dateAujourdhui= datetime.now().strftime('%d-%m-%Y')
evenements = session['evenements']
return render_template('events/showEvents.html', dateToday=dateAujourdhui, events = evenements)
@app.route('/events/details/<int:id>', methods=['GET'])
def details_event(id):
evenements = session['evenements']
evenement=evenements[id-1]
print evenement
return render_template('events/detailsEvent.html', event = evenement)
voir partie précédente
"/events/add"
(elle porte comme nom, le nom de la fonction add_event)@app.route('/events/add', methods=['GET'])
def add_event():
dateAujourdhui = datetime.now().strftime('%Y-%m-%d')
return render_template('events/addEvent.html', dateAujourdhui = dateAujourdhui)
Event.validAddEvent
, la méthode de soumission du formulaire est “post”{% extends 'layout.html' %}
{% block body %}
<form action="{{ url_for('valid_add_event') }}" method="post">
<label for="nom"> Nom de l'évenement :</label><input type="text" name="nom"><br><br>
<label for="description"> description : </label><textarea name="description" cols="15" rows="4">
</textarea><br><br>
<label for="date">date de l'évenement {{ dateAujourdhui }} : </label>
<input type="date" name="date" value="{{ dateAujourdhui }}"
data-date="{{ dateAujourdhui }}" data-date-format="DD MM YYYY"><br>
prix : <input type="text" name="prix" ><br>
<input type="submit" value="valider">
</form>
<br><br>
<br><br><a href="{{ url_for('show_events') }}">retour listes événements</a>
{% endblock %}
Dans la vue events/showEvents.html, ajouter un lien pour appeler la route de nom valid_add_event avec le fonction url_for sur la fonction (route ) de nom valid_add_event
Créer une méthode valid_add_event
la méthode valid_add_event utilise en annotation la route d’URL /events/validAdd
et le nom valid_add_event
@app.route('/events/validAdd', methods=['POST'])
def valid_add_event():
nom = request.form['nom']
description = request.form['description']
date = request.form.get('date', '')
prix = request.form.get('prix', '')
evenements = session['evenements']
nbrEvt=len(evenements)
if (nbrEvt == 0):
lastId=0
else:
lastId= evenements[nbrEvt-1]['id']
newId = int(lastId) + 1
ligne={"id" : newId,"nom" : nom, "description" : description,"date" : date,"prix" : prix}
evenements.append(ligne)
session['evenements'] = evenements
return redirect(url_for('show_events'))
ajouter un lien dans la barre de navigation
<h1><a href="{{ url_for('add_event') }}"> Ajouter un événement </a></h1>
la méthode deleteEvent utilise en annotation la route "/events/delete"
de nom Event.deleteEvent
méthode : exemple de solution (à la fin du document, d’autres solutions) lors de la suppression d’un événement, créer un tableau temporaire avec tous les événements sauf l’événement d’identifiant passé en paramètre.a Puis remplacer le tableau $evenement
en session par ce tableau temporaire.
@app.route('/events/delete', methods=['POST'])
def delete_event():
id = request.form['id']
tmp = []
evenements = session['evenements']
for i in range(0,len(evenements)):
if str(evenements[i]['id']) != id:
ligne={"id" : evenements[i]['id'],"nom" : evenements[i]['nom'], "description" : evenements[i]['description'],"date" : evenements[i]['date'],"prix" : evenements[i]['prix']}
tmp.append(ligne)
session['evenements'] = tmp
flash(u'événement supprimé !')
return redirect(url_for('show_events'))
<form action="{{ url_for('delete_event') }}" method="post" style="display: inline">
<input type="hidden" value="{{ event.id }}" name="id">
<input type="hidden" name="_method" value="DELETE">
<button type="submit">supprimer</button>
</form>
/events/details/last
de nom details_last_event@app.route('/events/details/<int:id>', methods=['GET'])
def details_event(id):
evenements = session['evenements']
for i in range(0,len(evenements)):
if str(evenements[i]['id']) == str(id):
evenement = evenements[i]
return render_template('events/detailsEvent.html', event = evenement)
Essayer de rajouter des messages “flash” lors de la suppression ou la création d’un événement
Il suffit de recopier la documentation : http://flask.pocoo.org/docs/1.0/patterns/flashing/#message-flashing-pattern
Ajouter dans le contrôleur dans les bonnes méthodes une ligne de code pour informer l’utilisateur qu’un événement a bien été ajouté ou supprimé :
....
flash(u'événement ajouté !')
....
flash(u'événement supprimé !')
<body>
le code pour afficher tous les messages flash{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
.flashes{ color:green;}
https://developer.mozilla.org/fr/docs/Web/HTML/Element/Input/date
dans la vue rajouter un filtre
exemple de filtre sur flask
@app.template_filter('dt')
def _jinja2_filter_datetime(date_time_str, fmt=None):
if date_time_str != '':
date=datetime.datetime.strptime(date_time_str, '%Y-%m-%d')
if fmt:
return date.strftime(fmt)
else:
return date.strftime('%d-%m-%Y')
@app.route('/events/edit/<int:id>', methods=['GET'])
def edit_event(id):
evenements = session['evenements']
for i in range(0,len(evenements)):
if str(evenements[i]['id']) == str(id):
evenement = evenements[i]
return render_template('events/editEvent.html', event = evenement)
@app.route('/events/validEdit', methods=['POST'])
def valid_edit_event():
id = request.form['id']
nom = request.form['nom']
description = request.form['description']
date = request.form.get('date', '')
prix = request.form.get('prix', '')
ligne2={"id" : id,"nom" : nom, "description" : description, "date" : date, "prix" : prix}
tmp = []
evenements = session['evenements']
for i in range(len(evenements)):
if str(evenements[i]['id']) != id:
ligne={"id" : evenements[i]['id'],"nom" : evenements[i]['nom'], "description" : evenements[i]['description'],"date" : evenements[i]['date'],"prix" : evenements[i]['prix']}
tmp.append(ligne)
print ligne
else :
tmp.append(ligne2)
session['evenements'] = tmp
flash(u'événement modifié !')
return redirect(url_for('show_events'))
{% extends 'layout.html' %}
{% block body %}
<form action="{{ url_for('valid_edit_event') }}" method="post">
<label for="nom"> Nom de l'évenement :</label>
<input type="text" name="nom" value="{{ event.nom }}"><br><br>
<label for="description"> description : </label>
<textarea name="description" cols="15" rows="4">
{{ event.description }}
</textarea><br><br>
<label for="date">date de l'évenement : </label>
<input type="date" name="date" value="{{ event.date }}"
data-date="{{ event.date }}" data-date-format="DD MM YYYY"><br>
<label for="prix">prix : </label>
<input type="text" name="prix" value="{{ event.prix }}"><br>
<input type="hidden" name="id" value="{{ event.id }}">
<input type="submit" value="valider">
</form>
<br><br>
<br><br><a href="{{ url_for('show_events') }}">retour listes événements</a>
{% endblock %}
<a href="{{ url_for('edit_event', id = event.id) }}">edit</a>
Avoir des routes REST(full)){target=“_blanck“} lors des requêtes HTTP
https://flask-restful.readthedocs.io/en/0.3.5/quickstart.html
* faire un essai avec curl
curl http://localhost:5001/events/delete -d "id=1" -X DELETE -v
curl http://localhost:5001/events/validEdit -d "id=1" -d "nom=flask" -d "description=td" -d "prix=None" -d "date=None" -X PUT -v
<input type="hidden" name="_method" value="PUT">
/**
* @Route("/events/validUpdate", name="Event.validUpdateEvent", methods={"PUT"})
*/
@app.before_request
def before_request():
#session.clear()
app.logger.warning('before call')
if 'evenements' in session:
evenements = session['evenements']
else:
evenements = [
{'id' : 1,'nom' : u'Symfony Conference', 'description' : u'présentation de la conférence sur Symfony ', 'date' : datetime(2019,2,20), 'prix' : 10.5},
{'id' : 2,'nom' : u'Laravel Conference', 'description' : u'présentation de la conférence sur Laravel ', 'date' : datetime(2019,3,2), 'prix' : None},
{'id' : 3,'nom' : u'Django Conference', 'description' : u'présentation de la conférence sur Django ', 'date' : datetime(2019,3,25), 'prix' : 20},
{'id' : 4,'nom' : u'JAVA2EE Conference', 'description' : u'présentation de la conférence sur Java J2EE ', 'date' : datetime(2019,4,2), 'prix' : 30},
{'id' : 5,'nom' : u'Rails Conference', 'description' : u'présentation de la conférence sur Ruby on Rails ', 'date' : datetime(2019,4,26), 'prix' : 12},
]
session['evenements']=evenements
if 'monPanier' in session:
monPanier = session['monPanier']
else:
monPanier = []
session['monPanier']=monPanier
# panier
@app.route('/panier', methods=['GET'])
def panier_index():
monPanier = session['monPanier']
evenements = session['evenements']
return render_template('panier/panier.html', events = evenements, monPanier = monPanier)
@app.route('/panier/ajouter/<int:id>', methods=['GET'])
def panier_ajouter(id):
monPanier = session['monPanier']
if searchProduit(id):
tmp=[]
for i in range(0,len(monPanier)):
if str(monPanier[i]['id']) != str(id):
ligne={"id" : monPanier[i]['id'],"nom" : monPanier[i]['nom'], "prix" : monPanier[i]['prix'],
"qte" : monPanier[i]['qte'],"prix_total" : monPanier[i]['prix_total']}
tmp.append(ligne)
else:
prix = monPanier[i]['prix']
if prix == None:
prix_total = None
else:
prix_total = monPanier[i]['prix']*(monPanier[i]['qte']+1)
ligne={"id" : monPanier[i]['id'],"nom" : monPanier[i]['nom'], "prix" : monPanier[i]['prix'],
"qte" : monPanier[i]['qte']+1,"prix_total" : prix_total}
tmp.append(ligne)
session['monPanier'] = tmp
else:
produitAajouter = infoProduit(id)
print produitAajouter
eventPanier = {"id" : produitAajouter['id'],"nom" : produitAajouter['nom'], "prix" : produitAajouter['prix'],
"qte" : 1 ,"prix_total" : produitAajouter['prix']}
monPanier.append(eventPanier)
session['monPanier'] = monPanier
return redirect(url_for('panier_index'))
@app.route('/panier/supprimer/<int:id>', methods=['GET'])
def panier_supprimer(id):
monPanier = session['monPanier']
tmp=[]
for i in range(0,len(monPanier)):
if str(monPanier[i]['id']) != str(id):
print str(monPanier[i]['id']) , str(id)
ligne={"id" : monPanier[i]['id'],"nom" : monPanier[i]['nom'], "prix" : monPanier[i]['prix'],
"qte" : monPanier[i]['qte'],"prix_total" : monPanier[i]['prix_total']}
tmp.append(ligne)
else:
if monPanier[i]['qte']>1:
prix = monPanier[i]['prix']
if prix == None:
prix_total = None
else:
prix_total = monPanier[i]['prix']*(monPanier[i]['qte']-1)
ligne={"id" : monPanier[i]['id'],"nom" : monPanier[i]['nom'], "prix" : monPanier[i]['prix'],
"qte" : monPanier[i]['qte']-1,"prix_total" : prix_total}
tmp.append(ligne)
session['monPanier'] = tmp
return redirect(url_for('panier_index'))
def searchProduit(idProduit):
monPanier = session['monPanier']
if monPanier == []:
return False
for i in range(0,len(monPanier)):
if str(monPanier[i]['id']) == str(idProduit):
return True
return False
def infoProduit(idProduit):
evenements = session['evenements']
# print evenements
print idProduit
if evenements == []:
return False
for i in range(0,len(evenements)):
print idProduit, type(idProduit)
print evenements[i]['id'], type(evenements[i]['id'])
if str(evenements[i]['id']) == str(idProduit):
return evenements[i]
return False
@app.route('/panier/vider', methods=['GET'])
def panier_vider():
session['monPanier'] = []
monPanier = []
evenements = session['evenements']
return render_template('panier/panier.html', events = evenements, monPanier = monPanier)
{% extends "layout.html" %}
{% block body %}
<div class="col-md-6" style="width: 50%; float: left">
{% for event in events %}
<div>
<h4 style="display: inline"><a href="{{ url_for('details_event', id = event.id) }}" class="btn btn-primary bs">{{ event.nom }}</a></h4>
<strong>Prix: </strong>{{ event.prix }} €
<a href="{{ url_for('panier_ajouter', id = event.id) }}">réserver</a>
</div>
{% endfor %}
</div>
<div class="col-md-6" style="width: 60%">
<h3>Liste des billets pour les évenements dans mon panier</h3>
<a class="btn" href="{{ url_for('panier_vider') }}">Éffacer tout</a>
<table class="table" style="border: solid #8959A8 1px ">
<tr>
<th>Nom</th>
<th>Quantité</th>
<th>Prix</th>
<th>Total</th>
<th>Action</th>
</tr>
{% for panier in monPanier %}
<tr style="border: solid #56a83e 2pxpx ">
<td>
{{panier.nom}}
</td>
<td>{{panier.qte}}</td>
<td>{{panier.prix}}</td>
<td>{{panier.qte * panier.prix if panier.prix != None else 0}}</td>
<td>
<a href="{{ url_for('panier_supprimer', id = panier.id) }}">Supprimer</a>
</td>
</tr>
{% endfor %}
<tr>
<td>
<strong>Quantité de produits: </strong>
</td>
<td>
{% set qteP = 0 %}
{% for qte in monPanier %}
{% set qteP = qte.qte + qteP %}
{% endfor %}
{{ qteP }}
</td>
<td>
<strong>Quantité à payer:</strong>
</td>
<td>
{% set qtePa = 0 %}
{% for qte in monPanier %}
{#% set qtePa = (qte.qte * qte.prix + qtePa %#}
{#% set qtePa = (qte.qte * "$%.2f"|format(qte.prix|float) + qtePa %#}
{% endfor %}
{{ qtePa }}
</td>
<td></td>
</tr>
</table>
</div>
{% endblock %}
<h1> <a href="{#{ { url_for('multiplicationChart_index') }#}"> Multiplication </a> </h1>
<h1><a href="{{ url_for('panier_index') }}"> Billeterie (Panier d'événements) </a></h1>
ANNEXES
ANNEXES : installation documentation
routing http://flask.pocoo.org/docs/1.0/quickstart/#routing
UUID https://fr.wikipedia.org/wiki/Universal_Unique_Identifier
{#{url_for('function_name', var=var)}#}
différence héritage
http://jinja.pocoo.org/docs/2.10/templates/#child-template
super au lieu de parent
les filtres
http://jinja.pocoo.org/docs/2.10/templates/#builtin-filters
message flash
http://flask.pocoo.org/docs/1.0/patterns/flashing/#message-flashing-pattern
http://flask.pocoo.org/docs/1.0/genindex/
bon tuto sur : https://realpython.com/python-web-applications-with-flask-part-i/
https://fraoustin.fr/old/python_flask.html
https://gist.github.com/doobeh/3e685ef25fac7d03ded7
api dto asp.net c#
https://docs.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#route-prefixes https://docs.microsoft.com/en-us/aspnet/web-api/overview/data/using-web-api-with-entity-framework/part-5