Exemple 2 : affichage de tableaux associatifs

(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 %}

passer des paramètres à la vue

@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

un tableau associatif à une dimension

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

un tableau associatif à 2 dimensions

    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.nom }}
{% else %}
pas d’événement
{% endfor %}

liens, nom de route


@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

paramètre dans une 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>

héritage dans les vues (aller un peu plus loin)

<!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).

front, front-office

mkdir static
touch static/essai.html  static/mesStyles.css

fonction “asset”

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


Gestion des évènements dans une variable en session



(à chaque nouvelle version de mozilla, ça change)



#! /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
@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)

ajouter des événements dans le tableau en session

voir partie précédente

affichage du formulaire

@app.route('/events/add', methods=['GET'])
def add_event():
    dateAujourdhui = datetime.now().strftime('%Y-%m-%d')
    return render_template('events/addEvent.html', dateAujourdhui = dateAujourdhui)


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

validation du formulaire

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

supprimer des événements en session

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

voir le détail du dernier événement en session

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

Messages “Flash”

Essayer de rajouter des messages “flash” lors de la suppression ou la création d’un événement

....
flash(u'événement ajouté !')

....
flash(u'événement supprimé !')
{% with messages = get_flashed_messages() %}
  {% if messages %}
    <ul class=flashes>
    {% for message in messages %}
      <li>{{ message }}</li>
    {% endfor %}
    </ul>
  {% endif %}
{% endwith %}

les filtres avec les dates

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

modifier un évènement

@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 %}
                &nbsp;
                <a href="{{ url_for('edit_event', id = event.id) }}">edit</a>
                &nbsp;

Exercice 3 : mettre les bons “verbes” pour chaque route

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"})
*/








le panier (exemple d’algorithme)




@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://openclassrooms.com/fr/courses/4425066-concevez-un-site-avec-flask/4525912-ajoutez-une-nouvelle-table-dans-la-base-de-donnees

https://fraoustin.fr/old/python_flask.html

https://python-django.dev/page-apprendre-listes-list-tableaux-tableaux-liste-array-python-cours-debutant

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