Vous êtes sur la page 1sur 350

Programmation Python pour le Web

Année universitaire : 2021-2022


Caractéristiques du langage Python
• Python est portable, non seulement sur les différentes variantes d'UNiX,
mais aussi sur les OS propriétaires: MacOS, différentes variantes de
Windows,…..
• Python est gratuit, mais on peut l'utiliser sans restriction dans des projets
commerciaux.
• Python convient aussi bien à des scripts d'une dizaine de lignes qu'à des
projets complexes de plusieurs dizaines de milliers de lignes.
• La syntaxe de Python est très simple et, combinée à des types de données
évoluées (listes, dictionnaires,...), conduit à des programmes à la fois très
compacts et très lisibles. A fonctionnalités égales, un programme Python est
souvent de 3 à 5 fois plus court qu'un programme C ou C++ (ou même Java)
équivalent, ce qui représente en général un temps de développement de 5 à
10 fois plus court et une facilité de maintenance largement accrue.
• Python gère ses ressources (mémoire, descripteurs de fichiers...) sans
intervention du programmeur.
• Il n'y a pas de pointeurs explicites en Python.
• Python est (optionnellement) multi-threadé. 2
Caractéristiques du langage Python
• Python est orienté-objet. Il supporte l'héritage multiple et la surcharge des
opérateurs.

• Python intègre un système d'exceptions, qui permettent de simplifier


considérablement la gestion des erreurs.

• Python est extensible: on peut l'interfacer avec des librairies C existantes.

• La librairie standard de Python, et les paquetages contribués, donnent accès


à une grande variété de services.

• Python est un langage qui continue à évoluer, soutenu par une communauté
d'utilisateurs enthousiastes et responsables.

• Django est un framework python open-source pour le développement Web.

3
Python conserve la tête du classement IEEE
pour l’année 2021, loin devant Java
Le magazine IEEE Spectrum a publié l’édition 2021 de son classement annuel
des meilleurs langages de programmation selon les développeurs. Le site web a
recoupé plusieurs sources pour établir un classement interactif et déterminer la
popularité des différents langages selon les plateformes.
Python en tête pour la 5ème année consécutive

4
Les 6 meilleurs frameworks Python
Les frameworks facilitent la vie des développeurs en leur offrant une structure
pour le développement d’applications. Ils automatisent la mise en œuvre de
solutions communes, réduisant ainsi le temps de développement et permettant
aux développeurs de se concentrer sur la logique de l’application plutôt que sur
les éléments de routine.

Comment choisir le bon framework ?


D’une part, lorsque vous décidez quel framework utiliser, prenez en compte la
taille et la complexité de votre projet.
Si vous cherchez à développer un système de grande taille, riche en
fonctionnalités et en exigences, un framework complet pourrait être le bon choix.
Si votre application est plus petite et plus simple, vous devriez probablement
envisager un microframework.
5
Les 6 meilleurs frameworks Python
Les frameworks complets

Un framework complet est une solution tout-en-un avec des


bibliothèques configurées pour travailler ensemble de manière
transparente. Il prend en charge le développement de services dorsaux,
d’interfaces frontales et de bases de données.

Un framework complet fournit tout ce dont un développeur a besoin pour


créer une application.

Python offre plusieurs frameworks complets. En voici quelques-uns


parmi les plus populaires.

6
Les 6 meilleurs frameworks Python

Django est un framework Python open-source complet et gratuit. Il


tente d’inclure toutes les fonctionnalités nécessaires par défaut, au lieu
de les proposer sous forme de bibliothèques séparées.

Le même code fonctionne avec différentes bases de données et n’est


pas difficile à transférer d’une base de données à l’autre. Les
principales bases de données avec lesquelles Django travaille sont
PostgreSQL, MySQL, SQLite et Oracle, mais des pilotes tiers vous
permettent d’en utiliser d’autres également.

Avec Django, vous pouvez créer n’importe quelle application web, du


projet à petite échelle au site web complexe.

7
Les 6 meilleurs frameworks Python

Pyramid est une application web basée sur Python, en open-source, et


le deuxième framework le plus populaire. Son but est de faire le plus
possible avec un minimum de complexité.

TurboGears est un framework d’application web open source, construit


sur de nombreux intergiciels et de nombreuses bibliothèques et a été
initialement conçu pour combiner les meilleurs composants des autres
frameworks Python.
TurboGears vous permet de développer rapidement des applications
web extensibles.
8
Les 6 meilleurs frameworks Python

Les microframeworks

Un microframework, ou framework minimaliste, ne possède pas la


plupart des fonctionnalités d’un framework complet.

Un microframework sert à fournir uniquement l’ensemble des


composants nécessaires à la construction d’une application.

9
Les 6 meilleurs frameworks Python

Flask est un framework Python disponible sous la licence libre BSD


(Berkeley Software Distribution License).
L’idée principale derrière Flask est d’aider à construire une base
solide d’applications web. De là, vous pouvez utiliser toutes les
extensions dont vous pourriez avoir besoin. Flask est choisi pour tous
types de projets. En fait, c’est un choix par défaut pour tout projet web
qui ne correspond pas à Django.

10
Les 6 meilleurs frameworks Python

Initialement destiné à la construction d’API, Bottle implémente tout dans un


seul fichier source. Il n’a pas de dépendances, à part la Python Standard
Library.

Attention: Bottle ne convient que si vous créez une très petite application
avec au maximum 500 lignes de code et sans exigences particulières.

Depuis 2009, Bottle a été mis à jour 73 fois, et c’est maintenant


une solution parfaite pour le prototypage, l’apprentissage des frameworks
web et la création d’applications personnelles simples.

11
Les 6 meilleurs frameworks Python

CherryPy est un framework web minimaliste open-source. Il permet de


construire des applications web en Python comme n’importe quel autre
programme orienté objet.

CherryPy est créé pour être extensible. Il a son propre serveur web, en
fait, une application web fonctionnant avec CherryPy est une application
python autonome qui intègre son propre serveur web multithread. Les
applications CherryPy fonctionnent sur tout système d’exploitation qui
supporte Python (Windows, macOS, Linux, etc.).

Vous n’avez pas besoin d’Apache pour les applications CherryPy, mais
vous pouvez exécuter une application CherryPy derrière Apache.

12
PyCharm

PyCharm est un environnement de développement intégré


(EDI) utilisé pour programmer en Python.

Il permet l'analyse de code et contient un débogueur graphique. Il


permet également la gestion des tests unitaires, l'intégration de logiciel
de gestion de versions, et supporte le développement web
avec Django.

Développé par l'entreprise tchèque JetBrains, c'est un logiciel multi-


plateforme qui fonctionne sous Windows, Mac OS X et Linux. Il est
décliné en édition professionnelle, diffusé sous licence propriétaire, et
en édition communautaire diffusé sous licence Apache.
13
PyDev

PyDev est un plug-in tiers pour Eclipse. Il s'agit d'un


environnement de développement intégré utilisé pour la
programmation en Python.

14
Compare Python code editors, IDE
Utiliser Pycharm
PyCharm est disponible en trois éditions:
- PyCharm Edu est gratuit et à des fins éducatives:
https://www.jetbrains.com/fr-fr/pycharm-edu/
- PyCharm Community est également gratuit et destiné au développement
python pur.
-PyCharm Professional est payé, a tout ce que l’édition communautaire
a et est également très bien adapté pour le développement Web et
scientifique avec un support pour des cadres tels que Django et Flask,
Database et SQL, et des outils scientifiques tels que Jupyter.

Pour plus de détails sur leurs différences, consultez la PyCharm Editions


Comparison Matrix de JetBrains. La société propose également offres
spéciales pour les étudiants, les enseignants, les projets open source et
d’autres cas.
Utiliser Pycharm
https://www.jetbrains.com/pycharm/buy/#discounts?billing=yearly
Utiliser Pycharm

18
Utiliser Pycharm

19
Utiliser Pycharm

La fonction factorial peut être utilisée sous: scipy.special.factorial(n) ou math.factorial(n)


1.Cliquez avec le bouton droit sur l’arrière-plan et choisissez Run ‘main’ dans le menu.
Ou
2. Puisque ce programme a la clause + main +, vous pouvez cliquer sur la petite flèche
verte à gauche de la clause + main + et choisir Run ‘main’ à partir de là.
20
Utiliser Pycharm
PyCharm provides methods for installing, uninstalling, and upgrading Python
packages for a particular Python interpreter. By default, PyCharm uses pip to
manage project packages.

File ➔ setting ➔ puis cliquer sur + en haut à droite


21
Utiliser Pycharm

Choisissez numpy, scipy et cliquer sur install package


22
Utiliser Pycharm

23
Les variables
• Type de variables
– entier (python: int) pour manipuler des entiers
– réel (python: float) pour manipuler des nombres
réels
– booléen (python: bool) pour manipuler des valeurs
booléennes
– Caractère (python: str) pour manipuler des caractères
alphabétiques ou numériques.

24
Les opérateurs et les expressions
• Les opérateurs: +, -, *, / Avec en plus pour les entiers div et mod,
qui permettent respectivement de calculer une division entière et le
reste de cette division.
par exemple :11 div 2 vaut 5, 11 mod 2 vaut 1
En Python: 11//2 vaut 5 et 11%3 vaut 2
• L’opérateur d’égalité :
pour les types simples il permet de savoir si les deux opérandes
sont égales ou non.
Il est représenté par le caractère = (en algo) et par (= =) en python
Le résultat d'une expression contenant cet opérateur est un booléen
• On a aussi l’opérateur d’inégalité : ≠ (en python !=)
• Et pour les types possédant un ordre les opérateurs de comparaison
<, ≤, >, ≥ 25
Les opérateurs logiques
Les operateurs logiques sont : ET ( and en python), OU (or en
python) et NON (not en python).

-Pour que la condition logique : condition1 ET condition2 soit


VRAI, il faut impérativement que la condition1 soit VRAI et que la
condition2 soit VRAI.

- Pour que la condition logique : condition1 OU condition2


soit VRAI, il suffit que condition1 soit VRAI ou condition2 soit
VRAI. Il est a noter que cette condition logique sera VRAI si
condition1 et condition2 sont VRAI.

- Le NON inverse une condition : NON(condition)


Est VRAI si condition est FAUX, et il sera FAUX si condition est
VRAI. 26
Les opérateurs et les expressions

27
Les entrées / sorties
• Instructions de lecture:
variable = (int, float, …) (input("votre message"))
Exemple:
>>> Nom=input('Donner votre nom:’)
Donner votre nom:'NOUH'
>>> print('Ton nom est:',Nom)
Ton nom est: 'NOUH'
>>> Annee=int(input("Donner l'anneé en cours:"))
Donner l'anneé en cours:2020
>>> print("L'année dernière c’était", Annee-1)
L'année dernière c’était 2019 28
Instruction conditionnelle
if (conditions 1): ATTENTION à l’indentation en Python !!!!

……………….
elif (conditions 2):
……………….
elif (conditions 3):
…………….
else :
……………….

Les blocs d'instructions sont toujours associés à une ligne


d'en-tête contenant une instruction bien spécifique (if, elif,
else, while, def, ...) se terminant par un double point. 29
Instruction conditionnelle
Exercice simple N°1:
Ecrire un programme python qui permet d'imprimer le
résultat d'un étudiant à un module sachant que ce module
est sanctionné par une note d'oral de coefficient 1 et une
note d'écrit de coefficient 2.
0≤moyenne obtenue<8 ➔ Echec
8≤moyenne obtenue<10 ➔ Rattrapage
10 ≤ moyenne obtenue ≤ 20 ➔ Module validé
20< moyenne obtenue ➔ Erreur de saisie

30
Les boucles for et while
• Il est fréquent que le nombre de répétitions soit connu à
l'avance, et que l'on ait besoin d'utiliser le numéro de l'itération
afin d'effectuer des calculs ou des tests.

for <cible> in <objet>:


<instructions>

• La boucle while exécute un bloc tant que la condition de


continuité est vérifiée.

while <test> :
<instructions

31
Les boucles for et while
• La boucle for fonctionne sur les chaînes, les listes, les tuples et
d’autres objets.
• La partie else: … est optionnelle.
• L’instruction range (a,b,pas) permet de créer une liste L
croissante ou décroissante d’entiers successifs . aL, a+pas L,
a+2*pas L, a+3*pas L, ….. Et x L: x<b
• Exemple:
for i in range(10,20,3):
print (i);
print("Sortie de la boucle avec i=",i);

10
13
Donne à l’exécution: 16
19
Sortie de la boucle avec i= 19 32
Instruction conditionnelle
Exercice 2:
-Ecrire un programme python qui permet de calculer le
produit 1*2*3*…*n pour un nombre entier n.

-- Ecrire un programme python qui permet de calculer la


somme 1+2+3+…+n pour un nombre entier n.

33
Les boucles for et while
Exercice 3:
programme qui compte le nombre de chiffres d’un nombre

a=int(input("Entrer un entier:"));
if a==0:
print("le nombre de chiffres est:",1);
else:
nbchif=0;
while a>0:
a=a//10;
nbchif=nbchif+1;
else:
print("le nombre de chiffres est:",nbchif);

# ou bien lent(str(a))

34
Les fonctions
Il arrivera souvent qu'une même séquence d'instructions doive être utilisée à plusieurs
reprises dans un programme, et on souhaitera bien évidemment ne pas avoir à la
reproduire systématiquement.

Nous avons vu diverses fonctions pré-programmées (pow, exp, …). Voyons à


présent comment on peut définir nous-mêmes de nouvelles. La syntaxe Python
pour la définition d'une fonction est la suivante:

def nomDeLaFonction(liste de paramètres):


...
bloc d'instructions
...
[resultat=…..] # […] c’est optionnel
[return resultat]

35
Les fonctions
>>> def table7():
... n=1
... while n <11 :
... Print( n * 7)
... n = n+1

Pour utiliser la fonction que nous venons de définir, il suffit de l'appeler par son
nom. Ainsi :

>>> table7()

Provoque l'affichage de :

7 14 21 28 35 42 49 56 63 70

36
Les fonctions
La fonction table() telle que définie ci-dessous utilise le paramètre base pour
calculer les dix premiers termes de la table de multiplication de base.

>>> def table(base):


n=1
while n <11 :
print (n * base)
n = n +1

Pour tester cette nouvelle fonction, il nous suffit de l'appeler avec un


argument.
>>> table(13)
13 26 39 52 65 78 91 104 117 130
>>> table(9)
9 18 27 36 45 54 63 72 81 90

37
Les fonctions
La fonction table() est certainement intéressante, mais elle n'affiche toujours que les dix
premiers termes de la table de multiplication, alors que nous pourrions souhaiter qu'elle en
affiche d'autres. Nous allons l'améliorer en lui ajoutant des paramètres supplémentaires,
dans une nouvelle version que nous appellerons cette fois tableMulti() :

>>> def tableMulti(base, debut, fin):


print (Fragment de la table de multiplication par', base, ':‘)
n = debut
while n <= fin :
print (n, 'x', base, '=', n * base)
n = n +1
Cette nouvelle fonction utilise donc trois paramètres : la base de la table comme dans l'exemple
précédent, l'indice du premier terme à afficher, l'indice du dernier terme à afficher.
Essayons cette fonction en entrant par exemple :
>>> tableMulti(8, 13, 17)
Fragment de la table de multiplication par 8 :
13 x 8 = 104
14 x 8 = 112
15 x 8 = 120
16 x 8 = 128
17 x 8 = 136 38
Utilisation des fonctions dans une autre fonction
Les fonctions peuvent aussi s'utiliser dans des scripts. Veuillez essayer vous-
même le petit programme ci-dessous, lequel calcule le volume d'une sphère à
l'aide de la formule : V= 4/3* pi*r^3

def cube(n):
return n**3

def volumeSphere(r):
return 4 * 3.1416 * cube(r) / 3

r = input('Entrez la valeur du rayon : ')


print ('Le volume de cette sphère vaut', volumeSphere(r))

Ce programme comporte trois parties : les deux fonctions cube() et


volumeSphere(), et ensuite le corps principal du programme.
Dans le corps principal du programme, il y a un appel de la fonction
volumeSphere().
A l'intérieur de la fonction volumeSphere(), il y a un appel de la fonction cube().

39
Fonction anonyme (lambda function)
Le mot-clé lambda en Python permet la création de fonctions anonymes
(i.e. sans nom et donc non définie par def)

Exemple:
>>> f = lambda x : x * x
>>> f ( 3 )
9
On peut également préciser plusieurs arguments:

>>> g = lambda x , y : x * y
>>> g(5, 7)
35
On peut également préciser des valeurs par défaut :

>>> g = lambda x , y=2 : x * y


>>> g ( 6 )
12

40
Fonctions comme valeurs de première classe
En Python, une fonction est une valeur comme une autre, c’est-à-dire qu’elle peut
être passée en argument, renvoyée comme résultat ou encore stockée dans une
variable. On dit que les fonctions sont des valeurs de première classe.
Une application directe est la définition d’un opérateur mathématique par une
fonction.

n
i =1
f (i )
Par exemple, la somme : , pour une fonction f quelconque, peut être
ainsi définie :

def somme_fonction( f , n ):
s = 0
for i in range(n+1):
s = s + f ( i )
return s

41
Exemple d’application:

Pour calculer la somme des carrés des entiers de 1 à 10, on


commence par définir une fonction carre.

def carre ( x ) :
return x*x

Puis, on la passe en argument à la fonction somme_fonction :

>>> somme_fonction ( carre , 10 )


385

42
Fonctions récursives
Une fonction est dite récursive si elle s’appelle elle-même : on parle alors d’appel
récursif de la fonction.

Exemple : Fonction factorielle


La fonction factorielle qui calcule le produit des n premiers entiers positifs:
n

(n! =
 k
)
k =1 est simplement définie par la relation de récurrence suivante:

0! = 1

n ! = n.(n − 1)! n  *

Version itérative : Version récursive :


def factorielle(n) : def factorielle(n) :
f = 1 if n==0 :
for k in range(2,n+1) : return 1
f = f * k else:
return f return n*factorielle(n-1)

43
Exemple d’exécution de la fonction récursive fact:

fact(6)=6 * fact(5) fact(6)=6 * 120

fact(5)=5 * fact(4) fact(5)=5 * 24

fact(4)=4 * fact(3) fact(4)=4 * 6

fact(3)=3 * fact(2) fact(3)=3 * 2

fact(2)=2 * fact(1) fact(2)=2 * 1

fact(1)=1*fact(0) fact(1)=1

On remarque bien les appels récursifs.


Pour calculer factorielle de i il faut attendre le calcul des factoriels des j qui lui sont
strictement inférieurs. Donc on descend et on remonte !
44
Avantages des fonctions
Voici quelques avantages d'un programme modulaire:
• Meilleure lisibilité
• Diminution du risque d'erreurs
• Possibilité de tests sélectifs
• Réutilisation de modules déjà existants
• Simplicité de l'entretien
• Favorisation du travail en équipe
• Hiérarchisation des modules

Procédure et fonction
Les "fonctions": assez proches de la notion mathématique correspondante.
Notamment, une fonction dispose d'arguments (en python, une fonction peut ne
pas comporter des arguments) qui correspondent à des informations qui lui sont
transmises et elle fournit un résultat qui peut être d’un type reconnu en python
(nombre, liste, tuple, …..
L’appel d’une fonction peut apparaître dans une expression.

✓ Les "procédures":. La procédure ne possède pas de valeur de retour et son appel


ne peut pas apparaître au sein d'une expression. Par contre, elle peut disposer
d'arguments. 4545
Les fonctions
Les fonctions peuvent aussi s'utiliser dans des scripts. Veuillez essayer vous-
même le petit programme ci-dessous, lequel calcule le volume d'une sphère à
l'aide de la formule : V= 4/3* pi*r^3

def cube(n):
return n**3

def volumeSphere(r):
return 4 * 3.1416 * cube(r) / 3

r = input('Entrez la valeur du rayon : ')


print ('Le volume de cette sphère vaut', volumeSphere(r))

Ce programme comporte trois parties : les deux fonctions cube() et


volumeSphere(), et ensuite le corps principal du programme.
Dans le corps principal du programme, il y a un appel de la fonction
volumeSphere().
A l'intérieur de la fonction volumeSphere(), il y a un appel de la fonction cube().

46
Les fonctions
Exercice 4:
1) En utilisant une boucle while, écrire la fonction logarithme_binaire, qui à un
réel strictement positif a associe :

2) Ecrire une fonction récursive en python f(n) permettant de calculer le produit


1*2*3*…*n pour un nombre entier n.

3) En Python, une fonction est une valeur comme une autre, c’est-à-dire
qu’elle peut être passée en argument. On dit que les fonctions sont des
valeurs de première classe.

n
Ecrire une fonction somme_fonction( f , n ) qui calcule la somme: i =1
f (i )

4) Ecrire une fonction python qui recherche le plus petit nombre entier naturel
dont le carré se termine par n fois le même chiffre.
Exemple : pour n = 2, le résultat est 10 car 100 se termine par 2 fois le
même chiffre

47
Les fonctions
Variable locale et variable globale
1. Variable locale
Les variables déclarées dans une fonction sont uniquement visibles à
l'intérieur de cette fonction. On dit que ce sont des variables locales.

2. Variable globale
Les variables déclarées à l'extérieur de toutes les fonctions sont disponibles
à toutes les fonctions du programme par l’utilisation du mot clé global. Ce
sont alors des variables globales.

Conseils :
▪ Les variables globales sont à utiliser avec précaution.
▪Il est conseillé d'écrire des programmes aussi localement que possible.

48 48
Les tableaux à une dimension
Comme tous les langages, Python permet d'utiliser des "tableaux". On nomme ainsi tableau
tout ensemble d'éléments de même type désigné par un identificateur unique; chaque
élément est repéré par un "indice" précisant sa position au sein de l'ensemble.

1 Déclarer un tableau
from array import array
X = array('i') # tableau d’entiers (int)
X = array(‘f') # tableau de réels (float)

2 Affecter des valeurs dans des cases


syntaxe : nom_tableau[numero_case] = valeur ;
La numérotation des cases s'effectue de 0 à nb_cases-1.

3 Accéder à la valeur d'une case d'un tableau


syntaxe : nom_tableau[numero_case]

4 Ajout d’un élément à la fin


nom_tableau.append(valeur à ajouter)

5 compter le nombre d’éléments


len(nom_tableau) 49 49
Les tableaux à une dimension
6. suppression:
from array import array; X = array('i');
X.append(2); X.append(1); X.append(12); X.append(3); X.append(0);
Print(X) ➔ array('i', [2, 1, 12, 3, 0])
del(X[2]); print(X) ➔ array('i', [2, 1, 3, 0])

7. inverser un tableau:
X.reverse(); print(X)
➔array('i', [0, 3, 2, 1])

8. insertion dans un tableau:


X.insert(1,555); print(X) # insère la valeur 555 dans la position 1
➔array('i', [0, 555,3, 2, 1])

9. Tri d’un tableau: 10. existance d’un élément:


X=sorted(X); print(X) 3 in X
➔ array('i', [0, 1, 2, 3, 555]) ➔ True

50 50
Les fonctions
Exercice 5:

1) Ecrire une fonction qui retourne le minimum des nombres positifs d’un
tableau T
2) La médiane d’un tableau A de valeurs réels est la valeur m telle que le
nombre de valeurs du tableau A supérieurs ou égales à m est égal au
nombre de valeurs inférieures ou égales à m. Pour déterminer la médiane
d'un tableau T il suffit :
De trier T par ordre croissant
Et de retourner la valeur qui est au centre du tableau T après le tri.
Donner une fonction def Mediane(T) qui retourne la médiane du tableau
T passé en paramètre.

3) Ecrire le code de la fonction : def Intersection_listes(T1, T2) qui


retourne la liste T3 contenant tous les éléments communs entre T1 et T2
sans répétitions.
Exemple : Si T1=[0.3 , 6, 19,33, 5, 11] et T2=[0.3 , 8, 16, 12,33]
alors :T3=[0.3 , 8, 33]. 51
Les chaines de caractères
Le type des chaînes de caractères, string en anglais et dans Python, est celui
permettant de représenter des textes. On considère dans un premier temps des
textes élémentaires, ceux composés d’une unique lettre ; on les appelle les
caractères.

En Python, les caractères peuvent être n’importe quelle lettre de l’alphabet, mais
aussi des symboles, comme les signes de ponctuation. Une chaîne de
caractères est une suite finie de caractères consécutifs, qu’on note entre
apostrophes ou guillemets :
'Ceci est une chaine'
La chaîne vide se note '' ou " "
Accès à un caractère:
On peut stocker une chaîne dans une variable :
s = 'Bonjour'
et accéder à chacun des caractères à l’aide de la construction s[i] :
>>>s[2]
'n’
Comparaison:
>>> ‘bonjour’ == ‘bonne journée’
False
52
Les chaines de caractères
les chaînes sont immuables
>>> s[0] = 'A'
TypeError: 'string' object does not support item assignment

Concaténation: On concatène deux chaînes à l’aide de l’opérateur + :


>>>'Bonjour '+ 'lecteur !'
'Bonjour lecteur !'

Longueur: On utilise len pour obtenir la longueur d’une chaîne :


>>>len('Bonjour')
7
Sous-chaînes
Un ensemble de caractères consécutifs à l’intérieur d’une chaîne s’appelle une
sous-chaîne. Ainsi, 'lecteur' ou 'jour lec' sont des sous-chaînes de 'Bonjour
lecteur !'.
Pour extraire une sous-chaîne de s, on écrit s[i:j] où i est l’indice du premier
caractère de la sous-chaîne et j est l’indice du dernier caractère plus un.
Exemples :
>>>s = 'Bonjour lecteur !'
>>>s[0:7] # de s[0] à s[6]
'Bonjour' 53
Les chaines de caractères
>>> ‘j' in 'Bonjour lecteur !‘
True
Il est à noter qu’il est également possible de tester la présence d’une sous-
chaîne dans une chaîne avec la même construction :
>>>'lecteur' in 'Bonjour lecteur !’ → True
>>>'Bjr' in 'Bonjour lecteur !’ → False
Conversion vers des types simples:
On peut convertir une valeur d’un type simple vers une chaîne de caractères à
l’aide de la construction str(e). La chaîne obtenue est la même que celle que
Python affiche par évaluation de e :
>>>str(1.2)
'1.2'
Il est possible de reconvertir une telle chaîne vers une valeur d’un type simple :
>>>int('123’) → 123
>>>float('1.2’) → 1.2
>>>bool('True') Code ASCII en Python:
True ord(‘A’)=65 et chr(65)=‘A’
>>> p=‘abcde’; q=p*3 ord(‘0’)=48et chr(48)=‘0’
q=‘abcdeabcdeabcde’
54
Les chaines de caractères
>>> x="Bonjour aux étudiants"
>>> y=x.split()
➔y= ['Bonjour', ‘aux', 'étudiants‘ ]

>>> n=x.count(‘o')
➔n=2
>>> x.endswith("ants")
➔True
>>> x.startswith("Bon")
➔True
>>>y =x.lower()
➔y= ="bonjour aux étudiants"
>>> y=x.upper()
➔‘BONJOUR AUX ETUDIANTS‘
>>>x.find(‘jour')
➔3
>>> x.index(‘jour')
➔3
55
Les chaines de caractères
>>> x="Bonjour mes chers"
>>> x.islower()
False >>> chaine = 'Aujourd'hui'
>>> chaine = 'Aujourd\'hui'
>>> x.isupper() → False
>>> print (chaine)
>>> x.isdigit() → False >>> chaine = "Aujourd'hui"
>>> x.isalpha() → False >>> print (chaine)
>>> x.isalnum() → False >>> chaine = 'Première ligne\nDeuxième ligne'
>>> y=x.replace(' ','') >>> print(chaine)
>>> y >>> X="Pa"
'Bonjourmeschers' >>> Y=X*2
>>> y.isalnum() → "PaPa"
True
>>> X="Bon"
>>> '6'.isdigit()
>>> Y=X*2
True → "BonBon"
>>>« 132455".isnumeric()
True

56
Les chaines de caractères
>>> " ".isspace()
➔True
>>> "Bonjour Monsieur".isspace()
➔False
>>> " ".isspace()
➔True
>>> "\t".isspace()
➔True
>>> "Titre".istitle()
➔True
>>> "TitrE".istitle()
➔False
>>> "Titre de mon site".istitle()
➔False
>>> "Titre De Mon Site".istitle()
➔True
>>> "***".join(["Master", "Data" , "Science"])
➔"Master***Data***Science"
➔>>> "Bonjour".replace("jour", " week-end")
➔ "Bon week-end") 57
Les chaines de caractères
>>>"Master\nBig\nData".splitlines() [‘Master', ‘Big', ‘Data']
>>> x="bonjour amine"
>>> x.capitalize()➔'Bonjour amine'
>>> x='bon'
>>> x.isprintable() ➔True
>>> x='bon'
>>> x.isprintable() ➔True
>>> y="c'estunevariable100%"
>>> y.isidentifier()➔False
>>> x="Bonjour Chers Etudiants"
>>> x.swapcase()➔'bONJOUR cHERS eTUDIANTS‘
>>> x="ceCi n'eSt pas BOn"
>>> x.title()➔"Ceci N'Est Pas Bon"
58
Les chaines de caractères

Exercice 6:

Ecrire une fonction supprimer_caractere (ch,x) qui enlève toutes les


occurences du caractère x dans la chaine ch.
Exemple:
Ch="Cette ligne contient quelques lettres e." et x="e"
supprimer_caractere (ch,x)➔ Cttlign contint qulquslttrs .

59
Les Listes
Définition d'une liste
C’est une collection ordonnée et modifiable d’éléments éventuellement hétérogènes.
>>> nombres = [5, 38, 10, 25]
>>> mots = ["Master", "Licence", "Doctorat"]
>>> L= [5000, "Big Data", 3.1416, ["Bon", "Jour", 1947]]
Dans le dernier exemple ci-dessus, nous avons rassemblé un entier, une chaîne,
un réel et même une liste, pour vous rappeler que l'on peut combiner dans une liste
des données de n'importe quel type, y compris des listes.
Accès aux éléments d’une liste
Pour accéder aux éléments d'une liste, on utilise les mêmes méthodes (index,
découpage en tranches) que pour accéder aux caractères d'une chaîne :
>>> print(nombres[2])
10 >>> print(nombres[:2])
>>> print(nombres[1:3]) [5, 38]
[38, 10] >>> print(nombres[-1])
>>> print(nombres[2:3]) 25
[10] >>> print(nombres[-2])
>>> print(nombres[2:]) 10
[10, 25]
60
Les Listes
Les listes sont modifiables
Les listes sont des séquences modifiables.
Exemples :
>>> nombres[0] = 17
>>> print(nombres)
[17, 38, 10, 25]
Les listes sont des objets
Sous Python, les listes sont des objets pour les quels on peut appliquer des méthodes
(fonctions) particulièrement efficaces. En voici quelques-unes :
>>> nombres = [17, 38, 10, 25, 72]
>>> nombres.sort() # trier la liste
>>> nombres
[10, 17, 25, 38, 72]
>>> nombres.append(12) # ajouter un élément à la fin
>>> nombres
[10, 17, 25, 38, 72, 12]
>>> nombres.reverse() # inverser l'ordre des éléments
>>> nombres
[12, 72, 38, 25, 17, 10]
61
Les Listes
>>> nombres.index(17) # retrouver l'index d'un élément
4
>>> nombres.remove(38) # enlever (effacer) un élément
>>> nombres → [12, 72, 25, 17, 10]
En plus de ces méthodes, vous disposez encore de l'instruction intégrée del, qui vous
permet d'effacer un ou plusieurs éléments à partir de leur(s) index :
>>> del(nombres[2])
>>> nombres → [12, 72, 17, 10]
>>> del(nombres[1:3])
>>> nombres → [12, 10]

Insertion d'un ou plusieurs éléments n'importe où dans une liste


>>> mots = ['jambon', 'fromage', 'confiture', 'chocolat']
>>> mots[2:2] =["miel"] # équivalent à mots=mots[:2] + ["miel"] + mots[2:]
>>> mots
['jambon', 'fromage', 'miel', 'confiture', 'chocolat']
>>> mots[5:5] =['saucisson', 'ketchup']
>>> mots
['jambon', 'fromage', 'miel', 'confiture', 'chocolat', 'saucisson', 'ketchup']
62
Les Listes
Suppression/remplacement d'éléments
>>> mots[2:5] = [] # [] désigne une liste vide
>>> mots
['jambon','fromage','saucisson', 'ketchup‘]
>>> mots[1:3] = ['salade']
>>> mots['jambon', 'salade', 'ketchup']
>>> mots[1:] = ['mayonnaise', 'poulet', 'tomate']
>>> mots['jambon', 'mayonnaise', 'poulet', 'tomate']

Création d'une liste de nombres à l'aide de la fonction range()


>>> L=list(range(10))
>>> L
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(5,13))
[5, 6, 7, 8, 9, 10, 11, 12]
>>> list(range(3,16,3))
[3, 6, 9, 12, 15]
Les arguments négatifs sont autorisés :
>>> list(range(10, -10, -3))
[10, 7, 4, 1, -2, -5, -8]
63
Les Listes
Parcours d'une liste à l'aide de for, range() et len()
L'instruction for est l'instruction idéale pour parcourir une liste :
>>> prov = ['La','raison','du','plus','fort','est','toujours','la','meilleure']
>>> for mot in prov:
... print(mot, end =' ')
La raison du plus fort est toujours la meilleure

Si vous voulez parcourir une gamme d'entiers, la fonction range() s'impose :


>>> for n in range(10, 18, 3):
... print(n, n**2, n**3)...

10 100 1000
13 169 2197
16 256 4096

Une liste en mode compréhension :


>>>S=[i**2 for i in range(4)]
>>> S → [0, 1, 4, 9]
>>> L1=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> L2=[x for x in L1 if x%3==0]
>>> L2 → [0, 3, 6, 9] 64
Les Listes
Opérations sur les listes
On peut appliquer aux listes les opérateurs + (concaténation) et * (multiplication) :
>>> fruits = ['orange','citron']
>>> legumes = ['poireau','oignon','tomate']
>>> fruits + legumes
['orange', 'citron', 'poireau', 'oignon', 'tomate']
>>> fruits * 3
['orange', 'citron', 'orange', 'citron', 'orange', 'citron']
L'opérateur * est particulièrement utile pour créer une liste de n éléments identiques :
>>> T = [0]*7
>>> T
[0, 0, 0, 0, 0, 0, 0]

Supposons par exemple que vous voulez créer une liste B qui contient le même
nombre d'éléments qu'une autre liste A. Vous pouvez obtenir ce résultat de différentes
manières, l'une des plus simples consiste à effectuer : B = [0]*len(A).
>>>'orange‘ in fruits
True
>>>len(fruits)
2
65
Les Listes
index(L,x,a,b) retourne la position de la première occurrence de x
dans la liste L entre les deux positions a et b. si b n’est pas donné la
recherche s’éffectue de la position a à la fin.

index(L,x) retourne la position de la première occurrence de x dans


la liste L (de début à la fin).
>>> T=[6,4,8,4,9,4,7,4,9]
>>> m=T.index(4,2,7)
>>> m → 3
>>> m=T.index(4,6)
>>> m → 7
>>> m=T.index(4,8)
Traceback (most recent call last):
File "<console>", line 1, in <module>
ValueError: 4 is not in list
>>> m=T.index(9,8)
>>> m → 8
66
Les Listes
>>> T=[6,4,8,4,9,4,7,4,9]
>>> T.remove(T[3])
>>> T
[6, 8, 4, 9, 4, 7, 4, 9]
>>> del(T[2])
>>> T
[6, 8, 9, 4, 7, 4, 9]
>>> a=T.pop()
>>> a → 9
>>> T → [6, 8, 9, 4, 7, 4]
>>> b=T.pop(-2)
>>> T → [6, 8, 9, 4, 4]
>>> b=T.pop(1)
>>> b → 8

67
Les Listes
>>> T=[6,4,8,4,9,4,7,4,9]
>>> T.remove(T[3])
>>> T → [6, 8, 4, 9, 4, 7, 4, 9]
>>> del(T[2])
>>> T → [6, 8, 9, 4, 7, 4, 9]
>>> a=T.pop()
>>> a → 9
>>> T → [6, 8, 9, 4, 7, 4]
>>> b=T.pop(-2)
>>> T → [6, 8, 9, 4, 4]
>>> b=T.pop(1)
>>> b
8

68
Les Listes
>>> T=[6,4,8,4,9,4,7,4,9]
>>> T.append([1,2,3])
>>> T → [6, 4, 8, 4, 9, 4, 7, 4, 9, [1, 2, 3]]
>>> T.extend([7,8,9])
>>> T → [6, 4, 8, 4, 9, 4, 7, 4, 9, [1, 2, 3], 7, 8, 9]
>>> T.insert(2,10000) # insére 10000 dans la position 2 de
la liste T
>>> T
[6, 4, 10000, 8, 4, 9, 4, 7, 4, 9, [1, 2, 3], 7, 8, 9]

>>> L=["Bon","Jour","Monsieur"]
>>> C="***".join(L)
>>> C
'Bon***Jour***Monsieur’
69
Les Listes

Exercice 7:

Ecrire une fonction supprimer(L,x) qui enlève toutes les occurences de


l’élément x dans la liste L.

70
Les tuples
Vous savez qu’il n’est pas possible de changer les caractères au sein d’une chaîne
existante, alors que vous pouvez modifier les éléments d’une liste.
Un tuple est une collection d’éléments à l’intérieur de parenthèses séparés par des
virgules semblable à une liste mais il n’est pas modifiable.
Un tuple peut représenter une clé de dictionnaire ce qu’une liste ne peut faire.
Les tuples sont préférables aux listes partout où l’on veut être certain que les
données transmises ne soient pas modifiées par erreur.
>>> saisons = ("printemps", "été", "automne", "hiver")
>>> print(saisons)
('printemps', 'été', 'automne', 'hiver')
>>> print(saisons[2:3])
('automne',) Il faut toujours au moins
>>> print (("printemps",) + saisons[1:4]) une virgule pour définir
('printemps', 'été', 'automne', 'hiver') un tuple.
>>> saisons[2] = "Automne"

Traceback (most recent call last):


File "<pyshell#4>", line 1, in <module>
saisons[2] = "Automne"
TypeError: 'tuple' object does not support item assignment 71
Création et affectation d’un tuple (suite)
>>> un_tuple = () tuple vide
>>> print un_tuple
()
>>> tuple("abc")
('a', 'b', 'c')

Pour accéder aux valeurs d’un tuple, on utilise les crochets de la même façon
qu’avec les listes.

>>> Couleur = ("blanc", "bleu", "rouge", "mauve", "noir")


>>> print(Couleur[1:3])
('bleu', 'rouge')
>>> print(Couleur[:3])
('blanc', 'bleu', 'rouge')
>>> print(Couleur[3:])
('mauve', 'noir')

72
Modification d’un tuple

Cela ne peut se faire directement. Il faut créer un nouveau tuple à l’aide


d’anciens grâce à l’opérateur de concaténation ou de répétition.

>>> Teinte = Couleur[1], Couleur[2], Couleur[4]


>>> print(Teinte)
('bleu', 'rouge', 'noir')
>>> Teinte = Teinte[0:2] + ("vert",)
>>> print(Teinte * 2)
('bleu', 'rouge', 'vert', 'bleu', 'rouge', 'vert')

On peut modifier certains éléments d’un tuple si ces éléments sont modifiables.
>>> T = (5, [0, 1, 2])
>>> print (T)
(5, [0, 1, 2])
>>> T[0] = 6
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
T[0] = 6
TypeError: 'tuple' object does not support item assignment
>>> T[1][0] = -1
>>> print (T)
(5, [-1, 1, 2]) 73
Suppression d’un tuple ou des éléments d’un tuple
Il est impossible de supprimer individuellement les éléments d’un tuple.
On peut assembler un nouveau tuple en omettant les éléments indésirables.
Pour supprimer explicitement un tuple entier, on utilise del :
del (teinte)
Les opérateurs *, +, in, [], == et les fonctions intégrées len, max, min
se comportent exactement de la même façon avec les tuples qu’avec les listes.

Les méthodes de manipulation de listes : tri, remplacement, insertion, etc. – ne


sont pas implémentées.
Un ensemble d’éléments séparés par des virgules et écrits sans symboles
d’identification de type crochets pour les listes, parenthèses pour les tuples, etc.
est par défaut un tuple.
>>> "Abc", 99 >>> u, v = 0, 1
('Abc', 99) >>> u, v
>>> x = 23, 45, "q" (0, 1)
>>> x >>> print (u, v)
(23, 45, 'q') 01

>>> T = (2, 3)
Les fonctions intégrées list() et tuple() permettent >>> T = list(T)
de convertir une liste en tuple ou vice versa. >>> print (T)
[2, 3] 74
Exercice 8:
1) Construire des listes de chaines de caractères valeur et couleur.
La première comprend les valeurs possibles d’une carte : 1, 2, 3,
. …, 9, Valet, Dame, Roi, As ; la deuxième les quatre couleurs
possibles : Trèfle, Carreau, cœur, pique.

2) A l’aide de ces deux listes construire une liste paquet contenant


les 52 cartes du jeu.

Une carte sera codée par un couple. Par exemple, la Dame de Cœur
sera codée par le couple (‘Dame’,’Cœur’).
3) Ecrire une fonction afficherCarte qui prend en entrée un couple
correspondant à une carte et qui retourne la carte sous un format
compact, comme dans l’exemple ci-dessous :
>>>afficherCarte((‘Dame’, ‘Cœur’)) ➔ ‘DC’
>>>afficherCarte((‘3’,’Carreau’)) ➔ ‘3K’
>>>afficherCarte((‘As’, ’pique’)) ➔ ‘AP’
4) Ecrire une fonction Melanger(paquet) qui prend en paramètre la
liste paquet et la mélange d’une manière aléatoire.

75
Exercice 8:
5) Le bridge est un jeu pour 4 joueurs qui se joue avec un jeu de
52 cartes. On distribue l’ensemble des cartes aux joueurs. Chacun
reçoit ainsi 13 cartes qui forment ce que l’on appelle sa main. Les
joueurs forment deux équipes de deux et vont devoir dans un premier
temps essayer de communiquer le plus possible d’informations sur
leurs mains respectives via un système codifié d’enchères. Pour y
parvenir, chaque joueur doit évaluer au mieux la force de sa main. On
associe, pour ce faire, une valeur à certaines cartes :
✓ 1 point pour chaque valet
✓ 2 points pour chaque Dame
✓ 3 points pour chaque Roi
✓ 4 points pour chaque As

Enfin, l’ordre des cartes a une grande importance au bridge. L’ordre


est le suivant : pour chaque couleur, on a:
2<3<4<5<6<7<8<9<1<Valet<Dame<Roi<As

D’autre part, on décide que Trèfle<Carreau<Cœur<Pique.

76
Exercice 8:

Ecrire une fonction Python mains sans entrée qui renvoie un


quadruplet (a, b, c, d) où a, b, c et d représentent chacun une liste de
treize carte et forment donc la main de chacun des joueurs.

Exemple :
>>> m=mains()
>>> m[0]
[('As', 'Coeur'), (4, 'Coeur'), (8, 'Coeur'), (5, 'Pique'), (8, 'Carreau'),
(9, 'Pique'), (4,'Pique'), (10, 'Coeur'), (4, 'Carreau'), (3, 'Pique'), (10,
'Pique'), ('Roi', 'Pique'), ('Dame‘,'Coeur')]

✓ 1 point pour chaque valet


✓ 2 points pour chaque Dame
✓ 3 points pour chaque Roi
✓ 4 points pour chaque As

77
Exercice 8:

6) Ecrire une fonction triMain qui prend en entrée une main et qui
retourne la main triée suivant l’ordre usuel du bridge.

Exemple :
>>> triMain(m[0])
[(4, 'Carreau'), (8, 'Carreau'), (4, 'Coeur'), (8, 'Coeur'), (10, 'Coeur'),
('Dame', 'Coeur'), ('As', 'Coeur'), (3, 'Pique'), (4, 'Pique'), (5,
'Pique'), (9, 'Pique'), (10, 'Pique'), ('Roi', 'Pique')]
>>> triMain(m[2])
[(4, 'Trèfle'), (5, 'Trèfle'), (6, 'Trèfle'), ('Valet', 'Trèfle'), ('Dame', 'Trèfle'),
(5,'Carreau'), ('Dame', 'Carreau'), ('As', 'Carreau'), (2, 'Coeur'),
(6,'Coeur'), (7, 'Coeur'), (2, 'Pique'), ('Valet', 'Pique')]

78
Exercice 8:

7) Ecrire une fonction Python afficherMain qui prend en entrée une main
(de treize cartes) et affiche la main triée sous un format compact,
comme dans l’exemple ci-dessous:
2T-5T-10T-RT-6K-10K-RK-AK-8C-AC-4P-7P-10P
8) Ecrire une fonction Python force qui prend en entrée la main d’un
joueur (donc une liste de treize cartes) et qui retourne la force de
cette main calculée comme décrit ci-dessus (diapositive précédente)

Exemple :
>>> m[2]
[(4, 'Trèfle'), ('Dame', 'Trèfle'), (5, 'Trèfle'), (2, 'Coeur'), ('As',
'Carreau'), (7, 'Coeur'), (6, 'Coeur'),
('Valet', 'Pique'), ('Valet', 'Trèfle'), (6, 'Trèfle'), (2, 'Pique'), (5, 'Carreau'),
('Dame', 'Carreau')]
>>> force(m[2])
10

79
Exercice 8:

Il convient également de déterminer si une couleur est totalement


absente (chicane) ou présente seulement une fois (singleton) ou au
contraire si une couleur est dominante(au moins 7 cartes dans cette
couleur).
9) Ecrire une fonction Python nombreCartesCouleur qui prend en entrée
une main m et une couleur c et qui retourne le nombre de cartes de la
couleur c contenues dans la main m.
10) Ecrire une fonction Python chicane qui prend en entrée la main d’un
joueur (donc une liste de treize cartes) et qui affiche une chaine
de caractères indiquant les chicanes éventuelles de la main.
11) Ecrire une fonction Python singleton qui prend en entrée la main d’un
joueur (donc une liste de treize cartes) et qui affiche une chaine
de caractères indiquant les singletons éventuelles de la main
12) Ecrire une fonction Python dominante qui prend en entrée la main
d’un joueur (donc une liste de treize cartes) et qui affiche une chaine
de caractères indiquant les couleurs dominantes de la main du
joueur.

80
Exercice 8:
Ecrire une fonction Python partieBridge sans entrée qui affecte une main
de treize cartes à chacun des quatre joueurs puis qui affiche pour chacun
d’entre eux la main, sa force et ses particularités. Par convention, les
joueurs au bridge sont appelés Nord, Sud, Est et Ouest.
>>> PartieBridge() #Voici un exemple d’utilisation de partieBridge
Nord :
2T-5T-10T-RT-10K-RK-AK-7C-8C-AC-4P-7P-10P
Force de la main : 14
Est :
2K-4K-5K-6K-7K-8K-DK-VC-DC-2P-3P-9P-DP
Force de la main : 7
Chicane Trèfle
Dominante Carreau
Sud :
3T-4T- DT- AT -3K-2C-3C -5C -6C- 5P - 6P-VP-AP
Force de la main : 11
Singleton Carreau
Ouest :
6T-7T-8T-9T –VT- 9K -VK -4C- 9C -10C –RC- 8P -RP
Force de la main : 8 81
Dictionnaires
Les types composites (chaînes de caractères, listes et tuples) considérés jusqu’à
maintenant étaient tous des séquences, i.e. des suites ordonnées d’éléments.

On peut accéder à un élément d’une séquence à partir de sa position.

Un dictionnaire ressemble à une liste et est modifiable mais n’est pas une
séquence car les éléments enregistrés ne sont pas disposés dans un ordre
immuable.
On peut accéder à un élément d’un dictionnaire à partir d’une clé. Cette clé
peut être une chaîne, un nombre ou même d’un type composite sous certaines
conditions. On ne peut pas modifier les clés d’un dictionnaire.

Puisque le type dictionnaire est un type modifiable, nous pouvons commencer


par créer un dictionnaire vide noté {}, puis le remplir par la suite.
>>> traduction = {}
>>> print(traduction) dictionnaire vide
{}
>>> traduction["mouse"] = "souris" insertion d’éléments à l’aide de
>>> traduction["keyboard"] = "clavier" paires clé-valeur
>>> print(traduction)
{'mouse': 'souris', 'keyboard': 'clavier'} 82
Le dictionnaire est entouré de {}.
Les dictionnaires
On peut aussi créer un dictionnaire comme suit :
>>> D = {"kh" : "khalil", "ch" : "chrit"}
>>> F, G = {}, {5: "Mauve", 2: "Rouge"}
>>> print (D, F, G)
{"kh" : "khalil", "ch" : "chrit"}{} {2: 'Rouge', 5: 'Mauve'}
L’ordre dans lequel les éléments apparaissent à l’affichage du dictionnaire ne
correspond pas nécessairement à celui dans lequel nous les avons insérés.
Cela n’a pas d’importance car nous utilisons les clés pour accéder à un élément.
>>> Couleur = {1: "Rouge", 2: "Vert", 3: "Bleu"}
>>> print(Couleur)
{1: 'Rouge', 2: 'Vert', 3: 'Bleu'}
>>> Couleur = {'1': "Rouge", '2': "Vert", '3': "Bleu"}
>>> print(Couleur)
{'1': 'Rouge', '3': 'Bleu', '2': 'Vert'}

Illustrons maintenant la variété de types de clés qu’on peut utiliser.


>>> D = {1: "un", '1': "valeur_unitaire", 1.1: "valeur_decimale"}
>>> print(D)
{'1': 'valeur_unitaire', 1: 'un', 1.1000000000000001: 'valeur_decimale'}
>>> D[1.1]
'valeur_decimale' 83
Les dictionnaires

Nous pouvons utiliser en guise de clés n’importe quel type de donnée non
modifiable : des entiers, des réels, des chaînes de caractères et même des tuples.

>>> sudoku = {}
>>> sudoku[(0, 1)] = 9
>>> sudoku[(0, 4)] = 5
>>> sudoku[(0, 7)] = 3
>>> print(sudoku)
{(0, 1): 9, (0, 7): 3, (0, 4): 5} Chaque clé dans un
>>> print(sudoku[(0,4)]) dictionnaire est unique.
5
>>> print(sudoku[(2,2)])
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
print sudoku[(2,2)]
KeyError: (2, 2)

Les dictionnaires permettent tous les opérateurs de type standard mais n’autorisent
pas d’opérations telles que la concaténation, la répétition ou l’extraction d’un groupe
d’éléments contigus car les dictionnaires ne sont pas des séquences, c’est-à-dire
les éléments ne sont pas disposés dans un ordre particulier. 84
Les dictionnaires
Contrairement à une liste, nous ne faisons pas appel à une méthode particulière
telle que append() pour ajouter un nouvel élément à un dictionnaire. Il suffit de
créer une nouvelle paire clé-valeur.
>>> print(traduction)
{'mouse': 'souris', 'keyboard': 'clavier'}
>>> traduction["computer"] = "ordinateur"
>>> print(traduction)
{'computer': 'ordinateur', 'mouse': 'souris', 'keyboard': 'clavier'}

dict() : renvoie un dictionnaire issu par exemple d’une séquence où ses éléments
doivent être associés deux à deux. Le premier élément constituera une
nouvelle clé et le second sa valeur associée.

>>> dict([[1,2], [6,7], [3,9]])


{1: 2, 3: 9, 6: 7}
>>> dict([(1,2), (6,7), (3,9)])
{1: 2, 3: 9, 6: 7}
>>> dict(((1,2), (6,7), (3,9)))
{1: 2, 3: 9, 6: 7}

85
Supprimer des éléments d’un dictionnaire ou des dictionnaires entiers

Pour enlever un élément à un dictionnaire, on utilise l’instruction del et pour


connaître le nombre d’éléments du dictionnaire, on se sert de la fonction len.
>>> del(traduction["mouse"])
>>> print (len(traduction))
2
>>> print(traduction)
{'computer': 'ordinateur', 'keyboard': 'clavier'}
>>> traduction.pop('computer') Supprime et retourne l’entrée.
'ordinateur'
>>> traduction.clear() Supprime toutes les entrées
>>> print(traduction) d’un dictionnaire.
{}
>>> del(traduction) Supprime un dictionnaire.
>>> print(traduction)

Traceback (most recent call last):


File "<pyshell#44>", line 1, in <module>
print traduction
NameError: name 'traduction' is not defined

86
Accès aux valeurs d’un dictionnaire

keys() : renvoie la liste des clés utilisées dans le dictionnaire.


Cela permet de parcourir un dictionnaire et d’accéder à ses valeurs.

>>> dictionnaire = {'rouge': 'red', 'vert': 'green', 'bleu': 'blue'}


>>> for cle in dictionnaire.keys():
print (cle, « --- » ,dictionnaire[cle])

bleu---blue
vert---green
rouge---red

Depuis Python 2.2, ce n’est plus nécessaire d’utiliser la méthode keys().

>>> dictionnaire = {'rouge': 'red', 'vert': 'green', 'bleu': 'blue'}


>>> for cle in dictionnaire:
print (cle, « --- » ,dictionnaire[cle])

bleu---blue
vert---green
rouge---red
87
On doit vérifier si un dictionnaire possède une clé avant de tenter d’accéder à
sa valeur.

>>> if 'noir' in dictionnaire: dictionnaire['noir']


>>> 'rouge' in dictionnaire
True

Comment modifier un dictionnaire ?

Vous pouvez mettre à jour un dictionnaire en lui ajoutant une nouvelle entrée ou
en modifiant une entrée existante.
>>> dictionnaire = {'rouge': 'red', 'vert': 'green', 'bleu': 'blue'}
>>> dictionnaire['noir'] = 'black'
>>> dictionnaire['bleu'] = 'BLUE'
>>> print(dictionnaire)
{'bleu': 'BLUE', 'noir': 'black', 'vert': 'green', 'rouge': 'red'}
Lorsqu’on affecte une valeur à une clé qui n’existe pas encore, la clé est créée
dans le dictionnaire et la valeur est ajoutée. Mais si la clé existe déjà, alors la
valeur existante est remplacée par la nouvelle.
>>> dictionnaire = {1: "vert", 1:"VERT"}
>>> print(dictionnaire)
{1: 'VERT'} 88
Fonctions et méthodes permettant de manipuler des dictionnaires.

D.values() : renvoie la liste des valeurs utilisées dans le dictionnaire D.

D.items() : renvoie une liste équivalente de tuples à partir d’un dictionnaire D.

>>> print(traduction.keys())
['computer', 'keyboard']
>>> print(traduction.values())
['ordinateur', 'clavier']
>>> print(traduction.has_key("mouse"))
False
>>> print(traduction.items())
[('computer', 'ordinateur'), ('keyboard', 'clavier')]
D.copy() : effectue une vraie copie d’un dictionnaire D au lieu d’un alias.
>>> conversion = traduction.copy()
>>> conversion["mouse"] = "souris"
>>> print traduction
{'computer': 'ordinateur', 'keyboard': 'clavier'}
>>> print conversion
{'computer': 'ordinateur', 'mouse': 'souris', 'keyboard': 'clavier'}
89
Exercice 9:
On dispose d’un dictionnaire associant à des noms de commerciaux
d’une société le nombre de ventes qu’ils ont réalisées.
Par exemple :
ventes= {"Amine":14, "Rachid":19, "Hamza":15, "Taha":21, "Aya":17}

1) Écrivez une fonction qui prend en entrée un dictionnaire et renvoie le


nombre total de ventes dans la société.

2) Écrivez une fonction qui prend en entrée un tel dictionnaire et renvoie


le nom du vendeur ayant réalisé le plus de ventes. Si plusieurs
vendeurs sont maximaux sur ce critère, la fonction devra retourner le
nom de l’un d’entre eux.

90
Ensembles
Définition d’un ensemble. Opérations sur les ensembles. Accès, suppression et
ajout d’éléments d’un ensemble. Fonctions permettant de manipuler les
ensembles.
Les ensembles en Python
Ce sont des ensembles non ordonnés de valeurs distinctes. Ces valeurs peuvent
être de différents types.

Les tests d’appartenance de même que les opérations d’union et d’intersection


fonctionnent comme attendu en Python.

Il est possible de tester l’appartenance à un ensemble à l’aide des opérateurs in et


not in, d’obtenir le cardinal d’un ensemble à l’aide de la fonction intégrée len() et de
parcourir les éléments d’un ensemble à l’aide de boucles.

Puisque les ensembles ne sont pas ordonnés, il n’est pas possible d’utiliser un
indice ni de sélectionner une tranche, et il n’existe pas de clé permettant d’accéder
aux valeurs.

Il existe 2 types d’ensembles :


set : On peut ajouter ou supprimer des éléments à ce type d’ensemble; par
conséquent, ils ne peuvent être utilisés comme clés de dictionnaire.
frozenset : On ne peut ajouter ou supprimer des éléments à ce type
d’ensemble; par contre, ils peuvent être utilisés comme clés
de dictionnaire ou comme membres d’un autre ensemble.
92
Symboles d’opérations et de relations s’appliquant aux ensembles

93
Comment créer des ensembles et leur affecter des valeurs ?
Il n’existe pas de syntaxe particulière pour les ensembles comme il en existe par
exemple pour les listes et pour les dictionnaires.

La seule méthode de création possible consiste à utiliser les fonctions set() et


frozenset().

>>> S = set("Nous sommes.")


>>> print(S)
set([‘o', ' ', ' s ', ' m ', ' u ‘, ' N‘, ‘e '])
>>> len(S)
7
>>> type(S)
<type 'set'>
>>> T = frozenset("Nous avons un ensemble de valeurs distinctes.")
>>> print(T)
frozenset(['a', ' ', 'c', 'b', 'e', 'd', 'i', 'm', 'l', 'o', 'N', '.', 's', 'r', 'u', 't', 'v', 'n'])
>>> len(T)
18
>>> type(T)
<type 'frozenset'>
>>> S == T
False
94
Comment créer des ensembles et leur affecter des valeurs ?
On peut créer un ensemble à l’aide d’une liste, d’un tuple ou d’un dictionnaire.

>>> Voyelles = set(["a", "e", "i", "o", "u", "y"])


>>> print(Voyelles)
set(['a', 'e', 'i', 'o', 'u', 'y'])

>>> Voyelles = set(("a", "e", "i", "o", "u", "y"))


>>> print(Voyelles)
set(['a', 'e', 'i', 'o', 'u', 'y'])

>>> Voyelles = set({0:"a", 1:"e", 2:"i", 3:"o", 4:"u", 5:"y"})


>>> print(Voyelles)
set([0, 1, 2, 3, 4, 5])
>>> Voyelles = set({0:"a", 1:"e", 2:"i", 3:"o", 4:"u", 5:"y"}.items())
>>> print(Voyelles)
set([(0, 'a'), (2, 'i'), (1, 'e'), (5, 'y'), (4, 'u'), (3, 'o')])
>>> Voyelles = set({0:"a", 1:"e", 2:"i", 3:"o", 4:"u", 5:"y"}.keys()) >>> set()
>>> print(Voyelles) set([])
set([0, 1, 2, 3, 4, 5]) >>> set([])
set([])
Si aucun argument n’est fourni, il y a création d’un ensemble vide. >>> set(())
set([])
Comment modifier un ensemble ?
1ière façon : ajouter un élément à un ensemble (add)

>>> voyelles = set("aeiou")


>>> voyelles.add("y")
>>> print (voyelles)
set(['a', 'e', 'i', 'o', 'u', 'y'])

2ième façon : ajouter des éléments à un ensemble (update)


>>> voyelles_et_chiffres = set("aeiouy")
>>> voyelles_et_chiffres.update("0123456789")
>>> print (voyelles_et_chiffres)
set(['a', '9', 'e', 'i', 'o', '1', '0', '3', '2', 'u', '4', '7', '6', 'y', '5', '8'])
>>>
>>> voyelles_et_chiffres = set("aeiouy")
>>> voyelles_et_chiffres.update(range(10))
>>> print(voyelles_et_chiffres)
set(['a', 0, 2, 3, 'e', 5, 6, 1, 'i', 9, 7, 'o', 8, 'u', 'y', 4])
>>>
>>> voyelles_et_chiffres = set("aeiouy")
>>> voyelles_et_chiffres.update([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> print(voyelles_et_chiffres)
set(['a', 0, 2, 3, 'e', 5, 6, 1, 'i', 9, 7, 'o', 8, 'u', 'y', 4]) 96
Comment modifier un ensemble ?
3ième façon : enlever un élément faisant partie d’un ensemble (remove),
enlever un élément à un ensemble s’il en fait partie (discard),
enlever et retourner un élément quelconque d’un ensemble (pop).
>>> chiffres = set(range(11))
>>> print(chiffres)
set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
>>> chiffres.remove(10)
>>> print(chiffres)
set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

4ième façon : enlever des éléments à un ensemble (-=)


>>> print(chiffres)
set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> chiffres -= set([4, 6, 8, 9])
>>> print chiffres
set([0, 1, 2, 3, 5, 7])

Comment supprimer un ensemble ?


Del(chiffres)
On peut aussi supprimer tous les éléments d’un ensemble avec la méthode clear().
97
Opérateurs sur les ensembles de même type ou de types différents
Appartenance (in, not in)
Sert à déterminer si un élément est ou n’est pas membre d’un ensemble.
>>> Prenoms = set([« Bon", « Jour"])
>>> "Jour" in Prenoms
True

Égalité / inégalité d’ensembles


Sert à tester l’égalité entre ensembles, c’est-à-dire si chaque élément de chaque
ensemble est aussi élément de l’autre ensemble. Les ensembles peuvent être de
même type ou de types différents.
>>> s = set(range(5,10))
>>> t = frozenset([5, 6, 7, 8, 9])
>>> s == t
True
>>> set("soir") != set("rois")
False

98
Sous-ensemble de ou sur-ensemble de (<, <=, >, >=)
Inégalité stricte :
A<B signifie que A est un sous-ensemble de B mais n’est pas égal.
A>B signifie que A est un sur-ensemble de B mais n’est pas égal.
Inégalité au sens large :
A <= B signifie que A est un sous-ensemble de B mais les 2 ensembles
peuvent être égaux.
A >= B signifie que A est un sur-ensemble de B mais les 2 ensembles
peuvent être égaux.
>>> groupeA = set([" AB", "CD", « EF"])
>>> groupeB = set([" EF", "AB", "CD", "MN", "FG"])
>>> groupeA < groupeB
True
Union (|)
Cette opération est équivalente au OU logique entre ensembles. L’union de 2
ensembles est un autre ensemble dont chaque élément appartient au moins à
l’un des 2 ensembles. union() est une méthode équivalente à cette opération.
>>> s = set("aeiouy") | set(range(10))
>>> print (s)
set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'e', 'i', 'o', 'u', 'y'])
>>> set("aeiouy").union(set(range(10)))
set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'e', 'i', 'o', 'u', 'y']) 99
Intersection (&)
Cette opération est équivalente au ET logique entre ensembles. L’intersection
de 2 ensembles est un autre ensemble dont chaque élément appartient aux 2
ensembles de départ. intersection() est une méthode équivalente à cette opération.
>>> set(range(10)) & set(range(5, 15))
set([8, 9, 5, 6, 7])

Différence ou complément relatif (-)


La différence de 2 ensembles est un >>> set("proximités") - set("permissivité")
autre ensemble dont chaque élément set(['x', 'o'])
appartient au premier ensemble mais
pas au second. difference() est une
méthode équivalente à cette opération.
Différence symétrique (^)
Cette opération est équivalente à un OU exclusif entre ensembles. La différence
symétrique de 2 ensembles est un autre ensemble dont chaque élément appartient
à l’un des ensembles mais pas à l’autre. symmetric_difference() est une méthode
équivalente à cette opération.
>>> set("proximités") ^ set("permissivité")
set(['e', 'o', 'v', 'x']) 100
Opérateurs s’appliquant uniquement aux ensembles de type set seulement
|= Cet opérateur ajoute un ou plusieurs membres d’un autre ensemble à l’ensemble
existant. update() est une méthode équivalente à cette opération.

>>> s = set("proximités")
>>> s |= set("permissivité")
>>> print(s)
set(['é', 'e', 'i', 'm', 'o', 'p', 's', 'r', 't', 'v', 'x'])

&= Cet opérateur ne conserve que les membres >>> s = set("proximités")


de l’ensemble existant qui sont également >>> s &= set("permissivité")
membres de l’autre ensemble. >>> print(s)
intersection_update() est une méthode équivalente set(['p', 'é', 'm', 'i', 's', 'r', 't'])
à cette opération.

-= Cet opérateur ne conserve que les membres de l’ensemble existant qui ne sont
pas membres de l’autre ensemble. difference_update() est une méthode
équivalente à cette opération.
>>> s = set("proximités")
>>> s -= set("permissivité")
>>> print(s)
set(['x', 'o']) 101
^= Cet opérateur ne conserve que les membres appartenant à l’un des deux
ensembles d’origine mais pas aux deux. symmetric_difference_update() est
une méthode équivalente à cette opération.
>>> s = set("proximités")
>>> s ^= set("permissivité")
>>> print s
set(['e', 'o', 'v', 'x'])

Fonctions intégrées
len() Retourne la cardinalité de l’ensemble.
>>> len(set("0123456789"))
10
>>> len(range(10))
10
>>> len(set(["a", "e", "i", "o", "u", "y"]))
6

102
Nous avons déjà mentionné l’existence de plusieurs méthodes s’appliquant aux
ensembles équivalentes à des opérateurs.

Il existe une méthode qui ne possède pas d’opérateur équivalent : copy();


elle retourne un nouvel ensemble qui est une copie de l’ensemble existant.

>>> s
set(['e', 'o', 'v', 'x'])
>>> t = s.copy()
>>> print t
set(['x', 'e', 'o', 'v'])
>>> t.add("u")
>>> print s
set(['e', 'o', 'v', 'x'])
>>> print t
set(['x', 'e', 'u', 'o', 'v'])

103
Sauvegarde des données d’un programme dans un fichier Fichiers
Nous allons considérer divers mécanismes permettant de créer
des fichiers,d’y envoyer des données et de les récupérer par après.

Étape #1. Choix du répertoire dans lequel le fichier sera créé.

Pour connaître le répertoire courant, on peut procéder ainsi :

>>> import os Le module os renferme


>>> repertoire_courant = os.getcwd() plusieurs fonctions permettant
>>> print(repertoire_courant) de dialoguer avec le système
C:\Python\libs d’exploitation.

La fonction getcwd() du module os


renvoie le nom du répertoire courant.

Vous pouvez changer de répertoire courant comme suit :


>>> import os
>>> import os >>> from os import *
>>> os.chdir("E:\\ProgrammesPython") >>> os.chdir("E:\\ProgrammesPython")
>>> print(os.getcwd()) Ou >>> print(getcwd())
E:\ProgrammesPython E:\ProgrammesPython
Ouverture d’un fichier en lecture et écriture séquentielle. Fichiers
>>> FichierLogique = open("E:\\ProgrammesPython\\essai", "a")
>>> FichierLogique.write("Si le fichier en écriture n'existe pas encore, il sera créé automatiquement. ")
>>> FichierLogique.write("Par contre, si le nom utilisé concerne un fichier préexistant ")
>>> FichierLogique.write("qui contient déjà des données, les caractères que vous y enregistrerez ")
>>> FichierLogique.write("viendront s'ajouter à la suite de ceux qui s'y trouvent déjà.")
>>> FichierLogique.close()

La fonction open possède 2 chaînes de caractères comme paramètres,


la première désigne le chemin d’accès menant au répertoire courant et
le nom du fichier à ouvrir (« essai »), la deuxième désigne le mode d’ouverture :
a: mode « append » qui consiste à ajouter les données à la fin
du fichier.
w: mode « write » qui entraîne la création d’un nouveau fichier
vide et l’écriture des nouvelles données.
S’il existe déjà un fichier de même nom, celui-ci est effacé
au préalable.
r: Mode « read », pour lire seulement
Au lieu de désigner explicitement le chemin d’accès au répertoire courant,
on peut le définir au début du programme comme précédemment.

La méthode write() permet l’écriture des données de manière séquentielle.


Il n’y a pas de séparateurs de ligne implicites.
La méthode close() permet la fermeture du fichier pour le réutiliser par la suite.
Lecture séquentielle d’un fichier de données.

Exemple : Affichage à l’écran du contenu d’un fichier.

>>> FichierLogique = open("E:\ProgrammesPython\essai", "r")


>>> Contenu = FichierLogique.read()
>>> print(Contenu)
Si le fichier en écriture n'existe pas encore, il sera créé automatiquement. Par
contre, si le nom utilisé concerne un fichier préexistant qui contient déjà des
données, les caractères que vous y enregistrerez viendront s'ajouter à la suite
de ceux qui s'y trouvent déjà.
>>> FichierLogique.close()

Mode d’ouverture en lecture (valeur par défaut si non spécifié)

Lecture des données du fichier et transfert dans une variable de type string.
Puisque la méthode read est utilisée sans argument et puisque nous n’avions
inséré aucun caractère de séparation, la totalité du fichier est transférée. Ce
code est raisonnable si la taille des fichiers n’est pas trop importante.

Si le fichier n’existe pas, cela produit une erreur à l’exécution.


FichierLogique est un identificateur désignant le fichier ouvert.

106
Note : La méthode read peut être utilisée avec un argument, le nombre de
caractères à lire, à partir de la position déjà atteinte dans le fichier.

>>> FichierLogique = open("E:\ProgrammesPython\essai", "r")


>>> print(FichierLogique.read(13))
Si le fichier
>>> print (FichierLogique.read(21))
en écriture n'existe
>>> print(FichierLogique.read(1000))
pas encore, il sera créé automatiquement. Par contre, si le nom utilisé concerne
un fichier préexistant qui contient déjà des données, les caractères que vous y
enregistrerez viendront s'ajouter à la suite de ceux qui s'y trouvent déjà.
>>> print (FichierLogique.read())

>>> FichierLogique.close()

Si la fin du fichier est déjà atteinte, read() renvoie une chaîne vide.

Si le nombre de caractères à lire est supérieur au nombre présent dans le fichier,


la lecture s’arrête à la fin du fichier.

107
Exemple : Copie d’un fichier dans un autre, 10 caractères à la fois.

>>> def CopieDeFichier(FichierSource, FichierDestination):


source = open(FichierSource, "r")
destination = open(FichierDestination, "w")
while 1: Boucle sans fin.
texte = source.read(10)
if texte == "":
break Interruption de la
destination.write(texte) boucle lorsque la
source.close() fin du fichier est
destination.close() rencontrée.

Un appel à cette fonction est réalisé comme suit :

>>> CopieDeFichier("E:\ProgrammesPython\essai", "E:\ProgrammesPython\copie")

f.tell() donne la position actuelle du curseur de lectures


f.fseek(n, 0) permet de positionner le curseur de lecture sur le nème
caractère en commençant de la tête du fichier (premier caractère).
Insertion de marqueurs de fin de ligne dans un fichier : \n

>>> F = open("E:\ProgrammesPython\Marqueurs", "w")


>>> F.write("Nous pouvons insérer un marqueur de fin de lignes\n")
>>> F.write("aux endroits où l’on souhaite séparer les lignes du texte.\n\n")
>>> F.write("Lors des opérations de lecture, les lignes d’un fichier texte ")
>>> F.write("peuvent être extraites séparément les unes des autres. La\n")
>>> F.write("méthode readline() ne lit qu’une seule ligne à la fois\n")
>>> F.write("(en incluant le caractère de fin de ligne).")
>>> F.close()
>>> F = open("E:\ProgrammesPython\Marqueurs", "r")
>>> print F.readline()
Nous pouvons insérer un marqueur de fin de lignes
>>> print F.readline()
aux endroits où l’on souhaite séparer les lignes du texte.
>>> LignesRestantes = F.readlines()
>>> print LignesRestantes
['\n', 'Lors des opérations de lecture, les lignes d’un fichier texte peuvent être
extraites séparément les unes des autres. La\n', 'méthode readline() ne lit qu’
une seule ligne à la fois\n', '(en incluant le caractère de fin de ligne).']
>>> F.close()

La méthode readlines() transfère toutes les lignes restantes dans une liste
de chaînes tandis que readline() renvoie une chaîne. 109
Exercice 10:
Pour sécuriser (d’une façon très simple) un message (ne contenant que
des lettres alphabétiques majuscules) que nous voulons transmettre on
peut le crypter par la méthode de césar de la façon suivante : la lettre A
devient D, B devient E, …, X devient A, Y devient B et Z devient C. La
figure suivante illustre ce cryptage.

a) Ecrire la fonction suivante : def Cesar(x) qui reçoit un caractère x et


revoie son correspondant comme expliqué dans la figure.
Exemples : Cesar(‘A’) renvoie ‘D’, Cesar(‘Y’) renvoie ‘C’,…

b) Ecrire une procédure: CrypterFichier(NomSource, NomFichCrypte)


qui reçoit en arguments le nom du fichier à crypter : NomSource (par
exemple "c:\\Message.txt") et le nom du fichier qui contiendra le texte
crypté : NomFichCrypte (par exemple F:\Message_Crypte.txt").
Cette procédure fait la lecture du fichier NomSource, à chaque fois qu’elle
lit un caractère, elle met Cesar(c) dans le fichier NomFichCrypte. 110
Gestion des Exceptions sous Python

L=[]
def Ajouter(L):
try:
x = int(input("Entrer un entier: "))
L.append(x)
except ValueError: # valeur non convertible par exemple
print("vous devez entrer un entier")
pass

def LectureFichier():
try:
f=open("D:\\message.txt","r")
print(f.read())
except IOError: # acces à un fichier qui n’existe pas par exemple
print("verifier le chemin d 'acces stp!!!!")
pass
Gestion des Exceptions sous Python

def f(a,b):
try:
f=a/b
print(f)
except ZeroDivisionError:
print("division par zero impossible, changer b")
pass

def modifier(L,i,x):
try:
L[i]=x
except IndexError: # Si i<0 ou i≥len(L)
print('indice incorrect')
pass
Bibliothèques mathématiques: Numpy, Linalg,…
• Les tableaux (arrays) sont des objets fournis par le module numpy.
Contrairement aux listes ils contiennent des objets de types identiques,
comme des flottants, des complexes ou des entiers.
• L’instruction : >>> from numpy import * permet de charger en mémoire
toute la bibliothèque numpy
• >>> T = array([0.1, 3, -1, 4, 6.7]) permet de convertir la liste [0.1, 3, -1, 4,
6.7] en un tableau de type array.
• >>> M=array([[0.4, 0.9, 4],[-1.5, 2, 1./3]]) permet de construire la matrice
[0.4, 0.9, 4]
[-1.5, 2, 1./3] à partir des deux listes [0.4, 0.9, 4] et [-1.5, 2, 1./3].

La liste =[[0.4, 0.9, 4],[-1.5, 2, 1./3],[1,3,6]] et la matrice


array([[ 0.4 , -1.5 , 1. ], [ 0.9 , 2. , 3. ], [ 4. , 0.33333333, 6. ]]) ont le même
contenu cependant l’utilisation de la forme matricielle numpy.array
permet d’exploiter plusieurs fonctions prédéfinies.

113
• >>> import numpy
• >>> A=[[0.4, 0.9, 4],[-1.5, 2, 1./3],[1,3,6]]
• >>> B=numpy.array(A)
• >>> B.trace()
8.4000000000000004
• >>> B.transpose()
array([[ 0.4 , -1.5 , 1. ],
[ 0.9 , 2. , 3. ],
[ 4. , 0.33333333, 6. ]])
• >>> A.trace()
• Traceback (most recent call last):File "<console>", line 1, in
<module> AttributeError: 'list' object has no attribute 'trace'
• >>> A.transpose()
Traceback (most recent call last):File "<console>", line 1, in
<module> AttributeError: 'list' object has no attribute 'transpose'
114
• >>> range(1,3,0.5)
Traceback (most recent call last):File "<console>", line 1, in
<module> TypeError: 'float' object cannot be interpreted as an
integer
• >>> arange(1,3,0.5)
array([ 1. , 1.5, 2. , 2.5])
• linspace(a,b,n) permet de créer un tableau dont les n
composantes sont uniformément réparties entre a et b.
• >>> from numpy import *
• >>> a=1;b=2;n=3;pas=(b-a)/(n-1)
• >>> linspace(a,b,n)
array([ 1. , 1.5, 2. ])
• >>> list(arange(a,b+pas,pas))
[1.0, 1.5, 2.0]

115
• >>> np.zeros(5)
array([ 0., 0., 0., 0., 0.])
• >>> np.ones((2,4)) # tableau 2d de taille 2 x 4 contenant des 1
array([[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.]])
• >>> B=numpy.array([[0.4, 0.9, 4],[-1.5, 2, 1./3],[1,3,6]])
• >>> C=zeros_like(B)
array([[ 0., 0., 0.], [ 0., 0., 0.],[ 0., 0., 0.]])
• >>> D=ones_like(B)
array([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]])

116
• >>> from numpy import *
• >>> T=random.rand(4) # pour un tableau 1d de 4 éléments
T= array([ 0.05662015, 0.58904519, 0.72276614, 0.13775529])
>>> T.dtype
dtype(’float64’)
• >>> X=random.randn(3,3) # pour une matrice aléatoire 3*3
• >>> X
array([[ 1.50331972, -0.56666019, -0.03043645],
[ 0.86226869, 0.07233328, 0.28352836],
[ 1.41434694, -0.67787242, 0.36466105]])
• >>> X=random.randint(0,9,5) # loi uniforme sur les entiers de
[0,9[ (donc [0,8])
• >>> #génération aléatoire de 5 entiers dans l'intervalle [0,8]
• >>> X
array([6, 7, 5, 5, 2]) 117
• >>> X=random.randint(0,9,(3,3)) # pour obtenir une matrice 3*3
à valeurs entières dans [0,8]
X= array([[3, 3, 5], [1, 2, 4], [2, 3, 1]])
• >>> T=diag(X,k=0); print(T)
[3 2 1]
• >>> T=diag(X,k=1); print(T)
[3 4]
• >>> T=diag(X,k=-1); print(T)
[1 3]
>>> M=eye(3) # matrice identité 3*3
M= array([[ 1., 0., 0.],
[ 0., 1., 0.],
[ 0., 0., 1.]])

118
• >>> T=numpy.array([3,2.6,8,7,9,100.5])
• >>> T.ndim
1
• >>> T.shape
(6,)
• >>> M=reshape(T,(3,2))
M=array([[ 3. , 2.6],
[ 8. , 7. ],
[ 9. , 100.5]])
• >>> M[1][0]= 44
• M=array([[ 3. , 2.6],
[ 44. , 7. ],
[ 9. , 100.5]])
• >>> T
array([ 3. , 2.6, 44. , 7. , 9. , 100.5]) 119
• >>> M=random.randint(0,10,(4,4))
array([[9, 5, 2, 9],
[4, 8, 0, 6],
[8, 8, 3, 6],
[9, 1, 7, 0]])
• >>> M[:,1]
array([5, 8, 8, 1])
• >>> M[2,:]
array([8, 8, 3, 6])
• >>> M[2,3]
6
• >>> N=10*M
>>> C=4*M+2*N
• >>> C
• array([[216, 120, 48, 216],
• [ 96, 192, 0, 144],
• [192, 192, 72, 144],
• [216, 24, 168, 0]]) 120
• >>> M=random.randint(0,10,(2,2))
• >>> N=random.randint(0,10,(2,2))
• >>> S=M+N
• >>> M
array([[4, 6],
[6, 2]])
• >>> N
array([[4, 7],
[2, 4]])
• >>> S
array([[ 8, 13],
[ 8, 6]])
• >>> P=M*N # produit élément par élément
• >>> P
array([[16, 42],
[12, 8]])
121
• la multiplication matricielle s’obtient avec la fonction dot :
• >>> A=random.randint(0,10,(2,2))
• >>> A
• array([[0, 6],
[9, 9]])
• >>> B=random.randint(0,10,(2,2))
• >>> B
• array([[0, 5],
[8, 2]])
• >>> C=dot(A,B)
• >>> C
array([[48, 12],
[72, 63]])
• >>> D=A.dot(B)
• >>> D
• array([[48, 12], [72, 63]])
122
• Concaténation de tableaux, on utilise la fonction concatenate.
• >>>A=random.randint(0,10,(2,2))
• >>> B=random.randint(0,10,(2,2))
• >>> C=concatenate ((A,B),axis=0)
• >>> D=concatenate((A,B),axis=1)
• >>> A array([[9, 0],
[2, 3]])
• >>> B
array([[2, 8],
[0, 0]])
• >>> C
• >>> D
array([[9, 0],
array([[9, 0, 2, 8],
[2, 3],
[2, 3, 0, 0]])
[2, 8],
[0, 0]]) 123
Les références partagées et le module copy
• Par erreur, il y a des personnes qui pensent que la manière la
plus simple de copier une liste est de déclarer une variable et de
lui affecter ladite liste comme dans l’exemple ci-dessous:
liste_initiale = [1, 6.3, [‘Amrani', ‘Yassine']]
copie_liste = liste_initiale
print(copie_liste)
Résultat : [1, 6.3, [‘Amrani', ‘Yassine']]
• Mais en faisant cela, nous avons simplement créé un alias, c’est-
à-dire que nous avons instancié un nouvel objet qui partage la
même référence que la liste copiée! Autrement dit nous avons
donné un deuxième nom copie_liste de la liste liste_initiale.
liste_initiale = [1, 6.3, ['Amrani', 'Yassine']]
copie_liste = liste_initiale
liste_initiale[0]=4
print(liste_initiale)
print(copie_liste)

[4, 6.3, ['Amrani', 'Yassine']]
[4, 6.3, ['Amrani', 'Yassine']]
On remarque clairement la dépendance entre la liste et sa copie.
Faire une shallow copy avec list() ou liste[:]
Essayons de voir si nous pouvons solutionner le problème en déclarant une
nouvelle liste et en lui affectant liste_initiale. Deux possibilités s’offrent à nous :

liste_initiale = [1, 6.3, ['Amrani', 'Yassine']]


copie_liste = list(liste_initiale)
Ou bien
copie_liste = liste_initiale[:]
Le schéma suivant résume la situation
liste_initiale = [1, 6.3, ['Amrani', 'Yassine']]
copie_liste1 = list(liste_initiale)
copie_liste2 = liste_initiale[:]
liste_initiale[0]=4
liste_initiale[2][0]='Saadi'
liste_initiale[2][1]='Anis'
print(liste_initiale); print(copie_liste1); print(copie_liste2)


[4, 6.3, ['Saadi', 'Anis']]
[1, 6.3, ['Saadi', 'Anis']]
[1, 6.3, ['Saadi', 'Anis']]
Avec le code ci-dessous, on arrive certes à faire une copie profonde
mais ce n’est pas l’idéal.
liste_initiale = [1, 6.3, ['Amrani', 'Yassine']]
copie_liste = list(liste_initiale); copie_liste[2] = list(liste_initiale[2])
liste_initiale[2][0]='Louhmadi'; liste_initiale[2][1]= 'Salim'
print(liste_initiale ); print(copie_liste)
➔ [1, 6.3, ['Louhmadi', 'Salim']]
[1, 6.3, ['Amrani', 'Yassine']]
Dans le cas d’une liste représentant un arbre (liste de listes de listes
de listes ….) la solution présentée en haut n’est pas très pratique!
Le module copy qui fonctionne avec tous les types mutables, permet
de faire deux sortes de copies différentes:
• la shallow copy (shallow signifie superficiel ou peu profond.)
• la deep copy (deep signifie profond.)
Pour utiliser ces deux méthodes, il faut importer le module copy:
• copy.copy effectue une shallow copy
• copy.deepcopy effectue une deep copy
• une shallow copy effectue une copie des éléments de premier
niveau. Elle copie les éléments 1 et 6.3 tandis que le troisième
élément (qui est une liste) devient une référence partagée. Cela
signifie que si vous remplacez « Amrani » par « Saadi » dans la
petite liste qui se trouve à l’indice 2 de la liste copie_liste, cette
modification affectera liste_initiale car ce n’est pas un élément de
premier niveau.
import copy
liste_initiale = [1, 6.3, ['Amrani', 'Yassine']]
copie_liste = copy.copy(liste_initiale)
liste_initiale[1]=7.15 ; liste_initiale[2][0]='Saadi';
liste_initiale[2][1]='Anis'
print(liste_initiale); print(copie_liste)
➔ [1, 7.15, ['Saadi', 'Anis']] [1, 6.3, ['Saadi', 'Anis']]
import copy
liste_initiale = [1, 6.3, ['Amrani', 'Yassine',[15,18]]]
copie_liste = copy.deepcopy(liste_initiale)
liste_initiale[1]=7.15 ; liste_initiale[2][0]='Saadi';
liste_initiale[2][1]='Anis' ; liste_initiale[2][2][1]=19
print(liste_initiale); print(copie_liste)
➔[1, 7.15, ['Saadi', 'Anis', [15, 19]]]
[1, 6.3, ['Amrani', 'Yassine', [15, 18]]]
Le module copy (avec ses méthodes shallow copy et deep copy)
fonctionne parfaitement avec d’autres objets mutables tels que les
dictionnaires.
import copy
dico = {"Nom" : "Amrani", 'Prénoms' : {'Prénom 1' : "Yassine",
'Prénom 2' : "Yasmine"}}
copie_dico = copy.deepcopy(dico)
copie_dico['Nom'] = "Saadi"
copie_dico['Prénoms']['Prénom 1'] = "Nissrine"
print(dico) ; print(copie_dico)

{'Nom': 'Amrani', 'Prénoms': {'Prénom 1': 'Yassine', 'Prénom 2':
'Yasmine'}}
{'Nom': 'Saadi', 'Prénoms': {'Prénom 1': 'Nissrine', 'Prénom 2':
'Yasmine'}}
Le même raisonnement s’applique sur les tuples:
T1=(1,2,3,[4,6,[7,8,9]]) ; T2=T1; T3=copy.deepcopy(T1)
T1[3][0]=7777; print(T1); print(T2);print(T3)
➔ (1, 2, 3, [7777, 6, [7, 8, 9]])
(1, 2, 3, [7777, 6, [7, 8, 9]])
(1, 2, 3, [4, 6, [7, 8, 9]])
Le même raisonnement s’applique sur les ensembles
import copy
S1=set(); S1.add(10); S1.add('Master M1-GL');S1.update({11,12,13})
S2=S1 ; S3=copy.deepcopy(S1)
S2.remove(10) ; S3.remove(12); print(S1); print(S2); print(S3)

{'Master M1-GL', 11, 12, 13}
{'Master M1-GL', 11, 12, 13}
{'Master M1-GL', 10, 11, 13}

Avec les objets de type str(), int() ou bien float(), il est inutile
d’utiliser le module copy puisque de toute façon, l’affectation va
créer un nouveau objet qui aura sa propre référence.
Base de données Sqlite sous python 3

Soit la base de données dont le shéma relationnel ci-dessous:

• clients( code_client,nom_complet, adresse ,type ,remise ,


date_naissanceDATE, fonction ,Société, Contact )
• Employe(N_employé,nom , prénom , Fonction , Date_d_embauche,
Date_de_naissance, code_postal, Ville , Code_du_superieur);
• commande(numero_commande ,numero_client*, date_commande,
num_employe* , a_livrer_avant)
• fournisseur(numero_fr ,nom_fr ,ville_fr);
• produit( ref_produit ,désignation ,pu ,famille ,numero_fr*, unites_en_Stock);
• details_commandes( numero_commande*, ref_produit*, Quantite );

135
import sqlite3 Connexion des bases de données avec Python
fichierDonnees = "C:/______________python cigma 20-21/DB1.sqlite"
conn=sqlite3.connect(fichierDonnees)
cur=conn.cursor()
cur.execute("INSERT INTO fournisseur
(numero_fr,nom_fr,ville_fr)VALUES(6401,'Ahmadi','meknes'),
(909,'A','B')")
cur.execute("SELECT * FROM fournisseur")
TableFournisseurs=cur.fetchall() # ou bien: list(cur)
cur.execute("delete FROM fournisseur where numero_fr=61")
conn.commit()
cur.close()
conn.close()

136
Base de données Sqlite sous python 3

L’opération commit() et rollback()

• Commit est l'opération, qui donne un signal vert à la base de


données pour finaliser les changements, et après cette
opération, aucun changement ne peut être renvoyé.

• Si vous n'êtes pas satisfait d'un ou de plusieurs des


changements et que vous souhaitez rétablir ces modifications
complètement, utilisez la méthode rollback().
Base de données Sqlite sous python 3
L’opération de lecture d’une base de données
• Une fois que notre connexion à la base de données est établie, vous
êtes prêt à effectuer une requête dans cette base de données. Vous
pouvez utiliser la méthode fetchone () pour extraire une valeur
unique ou fetchall () pour extraire plusieurs valeurs d'une table de
base de données.
– fetchone (): Il récupère la ligne suivante d'un ensemble de
résultats de requête. Un ensemble de résultats est un objet
renvoyé lorsqu'un objet curseur est utilisé pour interroger une
table.
– fetchall (): Il récupère toutes les lignes d'un jeu de résultats. Si
certaines lignes ont déjà été extraites du jeu de résultats, elles
récupèrent les lignes restantes du jeu de résultats.
– rowcount: Il s'agit d'un attribut en lecture seule et renvoie le
nombre de lignes affectées par une méthode execute ().
Base de données Sqlite sous python 3

• nombre de lignes affectés peut être obtenu avec cur.rowcount.


Attention, ce nombre est correct uniquement pour les
insert/update/delete. Il vaut -1 pour les select.

• en fait, on peut aussi utiliser directement execute et executemany


avec la connection sans ouvrir explicitement de curseur, c'est un
raccourci, par exemple : con.executemany("insert into myTable (x,
y) values (?, ?)", data).
Base de données Sqlite sous python
Exercice 11:
1) En utilisant fetchall, écrire une fonction python:
def ListeDesProduit(numFour) permettant d’afficher la liste des produits
du fournisseur numéronumFour. Tester cette fonction pour numFour=1.

2) En utilisant fetchone(), écrire une fonction python:


def InformationsProduit(REF) permettant d’afficher les informations du
produit de référence RE. Tester cette fonction pour REF =1.

3) En utilisant fetchone(), écrire une fonction python :


def InformationsProduit(DESIGN) permettant d’afficher les informations
du produit de désignation DESIGN.
Tester cette fonction pour DESIGN = "Clavier".

4) Ecrire une fonction python def InsererFournisseur(f) permettant d’insérer


le fournisseur f dans la table fournisseur. Donner à l’utilisateur de cette
fonction la possibilité de valider cette insertion ou de l’annuler. Tester cette
fonction pour f=(20,'Louhmadi','Mohammedia')
Base de données Sqlite sous python

def ListeDesProduit(numFour):
import sqlite3
fichierDonnees ="C:/DB1.sqlite"
conn=sqlite3.connect(fichierDonnees)
cur=conn.cursor()
requete="SELECT * FROM produit where numero_fr="+str(numFour)
cur.execute(requete)
resultat=cur.fetchall()
for r in resultat:
print(r)
cur.close()
conn.close() 141
Base de données Sqlite sous python

def InformationsProduit(REF):
import sqlite3
fichierDonnees = "C:/DB1.sqlite"
conn=sqlite3.connect(fichierDonnees)
cur=conn.cursor()
requete="SELECT * FROM produit where ref_produit="+str(REF)
print(requete)
cur.execute(requete)
resultat=cur.fetchone()
print(resultat)
cur.close()
conn.close() 142
Base de données Sqlite sous python

def InformationsProduit(DESIGN):
import sqlite3
fichierDonnees = "C:/DB1.sqlite"
conn=sqlite3.connect(fichierDonnees)
cur=conn.cursor()
requete="SELECT * FROM produit where désignation="+'"'+DESIGN+'"'
print(requete)
cur.execute(requete)
resultat=cur.fetchone()
print(resultat) ; cur.close() ; conn.close()

143
Base de données Sqlite sous python
four=(2999,'Louhmadi','Mohammedia'); f3=(112611,'Akrami','Casablanca')

def InsererFournisseur(f):
import sqlite3
fichierDonnees = "C:/DB1.sqlite"
conn=sqlite3.connect(fichierDonnees)
cur=conn.cursor()
requete="insert into fournisseur values"+str(f)
cur.execute(requete)
choix=input("Pour valider cette insertion taper O, sinon N:")
if choix=='O':
conn.commit()
else:
conn.rollback()
cur.close()
conn.close()

144
Introduction à la programmation Orienté Objet sous python 3

145
Introduction à la programmation Orienté Objet sous python 3

146
Introduction à la programmation Orienté Objet sous python 3

147
Introduction à la programmation Orienté Objet sous python 3

148
Introduction à la programmation Orienté Objet sous python 3
Introduction à la programmation Orienté Objet sous python 3
Introduction à la programmation Orienté Objet sous python 3

151
Introduction à la programmation Orienté Objet sous python 3

152
Surcharge d’opérateurs

Operateur Expression Interprétation Python


Addition p1 + p2 p1.__add__(p2)
Soustration p1 - p2 p1.__sub__(p2)
Multiplication p1 * p2 p1.__mul__(p2)
Puissance p1 ** p2 p1.__pow__(p2)
Division p1 / p2 p1.__truediv__(p2)
Division entière p1 // p2 p1.__floordiv__(p2)
le reste (modulo) p1 % p2 p1.__mod__(p2)

153
Surcharge d’opérateurs

Operateur Expression Interprétation Python


Inférieur à p1 < p2 p1.__lt__(p2)
Inférieur ou égal p1 <= p2 p1.__le__(p2)
Egal p1 == p2 p1.__eq__(p2)
différent p1 != p2 p1.__ne__(p2)
Supérieur à p1 > p2 p1.__gt__(p2)
Supérieur ou p1 >= p2 p1.__ge__(p2)
égal

154
Surcharge d’opérateurs

opération symbole méthode


opposé - __neg__(self)
positif + __pos__(self)
valeur absolue abs() __abs__(self

opération syntaxe méthode


dimension len(objet) __len__(self)
accès aux éléments
objet[key] __getitem__(self,key)
en lecture
accès aux éléments
objet[key] __setitem__(self,key,value)
en écriture
155
Introduction à la programmation Orienté Objet sous python 3
Introduction à la programmation Orienté Objet sous python 3
Introduction à la programmation Orienté Objet sous python 3

158
Surdéfinition de fonctions

def f(x):
print("je suis une fonction avec un seul paramètre:"+str(x))

def f(x,y):
print("je suis une fonction avec deux paramètres:"+str(x)+ '
et '+ str(y))

print(f(100))

Traceback (most recent call last):


File "C:\______________python
cigma…\Python….CIGMA\poo_cigma_partie_5_polymorphisme.
py", line 7, in <module>
print(f(100))
TypeError: f() missing 1 required positional argument: 'y' 159
Surdéfinition de fonctions

from multipledispatch import dispatch


@dispatch(object)
def f(x):
print("fonction avec un seul paramètre:"+str(x))
@dispatch(object,object)
def f(x,y):
print("fonction avec deux paramètres:"+str(x)+ ' et '+ str(y))

print(f(100))
print(f(1111,2222))
===========================➔
je suis une fonction avec un seul paramètre:100
je suis une fonction avec deux paramètres:1111 et 2222

160
Framework Django

161
Framework Django

162
Framework Django
Quels gros sites sont faits en Django ?

Disqus MichaelMoore.com
Pinterest newyorktimes.com
Instagram washingtonpost.com
Mahalo guardian.co.uk
bitbucket lawrence.com
lanyrd mercedesbenz.com
support.mozilla.com nationalgeographic.com
addons.mozilla.org discoverychannel.com
The Onion orange.ch
EveryBlock developer.vodafone.com
GiantBomb
ComicVine

163
Framework Django
Différences entre Django et php ?

Django est un framework et PHP est un langage de développement.


Django fournit des solutions pour un développement facile grâce à ses
templates, son mappage d’URL, ses vues génériques, etc. Il garantit que
ce framework permet un développement rapide.

Python et Django sont beaucoup plus rapides que PHP, Python peut
être très puissant, il est généralement utilisé pour les applications
complexes et volumineuses, mais il n’est pas aussi facile à déployer et à
maintenir que PHP. Donc, si vous faites du contenu Web traditionnel,
mieux vaut rester avec PHP. Les deux environnements ont leurs propres
avantages, il n’y a tout simplement pas de «meilleur». C’est plutôt
subjectif et dépend de vos besoins.

164
Framework Django

Django prend officiellement en charge les bases de données suivantes :

PostgreSQL.

MariaDB.

MySQL.

Oracle.

SQLite ( Nous allons travailler dans ce cours avec SQLite )

165
Framework Django

• Utilisation d’un moteur de base de données externe: en plus des


bases de données prises en charge officiellement, il existe des
moteurs externes à Django qui permettent d’utiliser d’autres
bases de données avec Django : CockroachDB, Firebird, Google
Cloud Spanner, Microsoft SQL Server
• Les versions de Django et les fonctionnalités ORM prises en
charges par ces moteurs inofficiels varient considérablement. Si
vous avez des questions concernant les capacités spécifiques de
ces moteurs inofficiels ou des questions de support, vous devrez
vous adresser aux canaux d’aide offerts par chacun de ces projets
externes.

166
Framework Django
Démarrer Pycharm Edu et créer un nouveau projet

167
Framework Django
Localiser votre projet dans un dossier: MonDossierDjango (à créer)

168
Framework Django
Environnements virtuels:
Imaginons que vous travailliez avec la version 1.0 de Librairie1 dans un projet X.
Six mois plus tard, vous avez de nouveau besoin de cette librairie mais la version
a changé, il s'agit désormais de la 1.1. Vous installez donc la nouvelle version qui,
elle-même, dépend d'autres modules ! Très vite, un château de cartes instable
s'installe dans votre ordinateur.
L'idéal, me direz-vous, serait de pouvoir isoler les librairies installées par projet.
Ou même mieux : de pouvoir décider quel environnement de développement
nous souhaitons utiliser.
C'est exactement l'objectif des environnements virtuels : créer un
environnement comprenant une certaine version de Python et les librairies que
vous souhaitez. Si votre ordinateur était un immeuble, nous pourrions imaginer
qu'un environnement virtuel serait une pièce de cet immeuble dans laquelle vous
installez ce que vous souhaitez. Lorsque vous entrez dans la pièce, les librairies
installées sont accessibles. Mais lorsque vous la quittez, plus rien n'existe !

169
Framework Django
La meilleure pratique de Python consiste à créer une virtualenv pour
chaque projet. Pour ce faire, développez le nœud Interpréteur Python:
Nouvel environnement Virtualenv et sélectionnez un outil utilisé pour
créer un nouvel environnement virtuel.

Choisissons l'outil Virtualenv et spécifions l'emplacement et l'interpréteur


de base utilisés pour le nouvel environnement virtuel. Cochez les deux
cases ci-dessous si nécessaire.

Virtual environment qui n'a pas accès aux packages de sites globaux. Ainsi,
l'interpréteur n‘est pas au courant des packages nouvellement installés.
➔ Pour résoudre le problème, modifiez ou recréez simplement votre
interprète virtuel et cochez l' Inherit global site-packages option.

170
Framework Django

171
Framework Django
Apprenons Django avec un exemple complet sur les sondages:
Nous allons créer une application simple de sondage. Cela consistera
en deux parties :
➢Un site public qui permet à des gens de voir les sondages et d’y
répondre.
➢Un site d’administration qui permet d’ajouter, de modifier et de
supprimer des sondages (coté base de données).
Nous supposerons que Django est déjà installé. Vous pouvez savoir si
Django est installé et sa version en exécutant la commande suivante
dans un terminal:
-> py -m django --version
172
Framework Django
Vérifier l’installation de Django:
C:\mes donnees\_____________________________________________python pour Web 21-
22\MonDossierDjango> py -m django --version
C:\mes donnees\_____________________________________________python pour Web 21-
22\MonDossierDjango\Scripts\python.exe: No module named django

Si ça ne marche pas, utiliser le terminal et taper la commande:


pip install django
Framework Django
Création d’un projet :
Depuis un terminal en ligne de commande, déplacez-vous à l’aide de la
commande cd dans un répertoire dans lequel vous souhaitez
conserver votre code (le dossier MonDossierDjango que vous avez
dèjà utilisé), puis lancez la commande suivante :
-> django-admin startproject mysite, comme ceci:
(venv) C:\mes donnees\_____________________________________________python
pour Web 21-22\MonDossierDjango>django-admin startproject mysite

-cd.. ➔ Permet de se positionner sur le répertoire père


-cd chemin ➔ permet le « change directory to chemin »
-C:\>cd c:\users\said nouh ➔c:\Users\said nouh>
Framework Django

Le premier répertoire racine mysite/ est un contenant pour votre projet. Son
nom n’a pas d’importance pour Django ; vous pouvez le renommer comme
vous voulez.
175
Framework Django

manage.py: un utilitaire en ligne de commande qui vous permet


d’interagir avec ce projet Django de différentes façons.

Le sous-répertoire mysite/ correspond au paquet Python effectif de


votre projet. C’est le nom du paquet Python que vous devrez utiliser
pour importer ce qu’il contient (par exemple: mysite.urls).

mysite/__init__.py : un fichier vide qui indique à Python que ce


répertoire doit être considéré comme un paquet.

mysite/settings.py : réglages et configuration de ce projet Django.

mysite/urls.py: les déclarations des URL de ce projet Django, une


sorte de « table des matières » de votre site Django.

176
Framework Django
Le serveur de développement:

Vérifions que votre projet Django fonctionne. Déplacez-vous dans le


répertoire mysite (qui contient manage.py) si ce n’est pas déjà fait, et
lancez la commandes suivante :

...\> py manage.py runserver


via Terminal:

C:\mes donnees\_____________________________________________python
pour Web 21-22\MonDossierDjango> cd mysite
C:\mes donnees\_____________________________________________python
pour Web 21-22\MonDossierDjango\mysite> py manage.py runserver

177
Framework Django
Vous verrez les messages suivants défiler en ligne de
commande :

Watching for file changes with StatReloader


Performing system checks...

System check identified no issues (0 silenced).

You have 18 unapplied migration(s). Your project may not work properly
until you apply the migrations for app(s): admin, auth, contenttypes,
sessions.
Run 'python manage.py migrate' to apply them.
November 08, 2020 - 12:49:23
Django version 3.1.3, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
====➔ CTRL-C.
178
Framework Django
Cliquer sur le lien: http://127.0.0.1:8000

Attention: ne pas avoir plusieurs applications ouvertes …Django….. 179


Framework Django
Modification du port

Par défaut, la commande runserver démarre le serveur de développement sur l’IP


interne sur le port 8000.

Si vous voulez changer cette valeur, passez-la comme paramètre sur la ligne de
commande. Par exemple, cette commande démarre le serveur sur le port 8080 :
...\> py manage.py runserver 8080

Rechargement automatique de runserver

Le serveur de développement recharge automatiquement le code Python lors de


chaque requête si nécessaire. Vous ne devez pas redémarrer le serveur pour que
les changements de code soient pris en compte. Cependant, certaines actions
comme l’ajout de fichiers ne provoquent pas de redémarrage, il est donc
nécessaire de redémarrer manuellement le serveur dans ces cas.

180
Framework Django

serveur de développement de Django:

Vous avez démarré le serveur de développement de Django, un serveur Web


léger entièrement écrit en Python. Il est inclus avec Django de façon à vous
permettre de développer rapidement, sans avoir à vous occuper de la
configuration d’un serveur de production – comme Apache – tant que vous n’en
avez pas besoin.

Dans ce cours, nous allons utiliser le serveur de développement intégré avec


Django.

Maintenant que le serveur tourne, allez à l’adresse http://127.0.0.1:8000 avec


votre navigateur Web. Vous verrez une page avec le message « Félicitations ! »
ainsi qu’une fusée qui décolle. Ça marche !

181
Framework Django
Création d’une application Polls (sondage):
Maintenant que votre environnement – un « projet » – est en place, vous êtes prêt
à commencer à travailler.
Chaque application que vous écrivez avec Django est en fait un paquet Python
qui respecte certaines conventions. Django est livré avec un utilitaire qui
génère automatiquement la structure des répertoires de base d’une
application, ce qui vous permet de vous concentrer sur l’écriture du code,
plutôt que sur la création de répertoires.

Dans ce qui suit, nous allons créer une application de sondage dans le même
dossier que le fichier manage.py pour qu’elle puisse être importée comme
module de premier niveau plutôt que comme sous-module de mysite.
Pour créer votre application, assurez-vous d’être dans le même répertoire
que manage.py et saisissez cette commande :
..\> py manage.py startapp polls
ou bien : django-admin startapp polls
ou bien : py -m django startapp polls

182
Framework Django
Cela va créer un répertoire polls, qui est structuré de la façon
suivante :

Cette structure de répertoire accueillera l’application de sondage.


183
Framework Django

Une fonction de vue (ou vue pour faire simple), est une fonction Python
acceptant une requête Web et renvoyant une réponse Web. Cette
réponse peut contenir le contenu HTML d'une page Web, une redirection,
une erreur 404, un document XML, une image… ou n'importe quoi d'autre.

Les objets requête et réponse, Lorsqu’une page est demandée, Django


crée un objet HttpRequest contenant des métadonnées au sujet de la
requête. Puis, Django charge la vue appropriée, lui transmettant
l’objet HttpRequest comme premier paramètre.

Chaque vue est responsable de renvoyer un objet HttpResponse.

184
Framework Django
Objets HttpResponse: Attributs
Contrairement aux objets HttpRequest qui sont automatiquement créés par Django,
les objets HttpResponse sont de votre responsabilité en tant que développeur.
Chaque vue que vous écrivez est responsable d’instancier, de remplir et de
renvoyer un objet HttpResponse.
La classe HttpResponse se trouve dans le module django.http.

Utilisation: Transmission de chaînes:


L’utilisation typique est de transmettre le contenu de la page comme une chaîne,
une chaîne d’octets au constructeur de HttpResponse:

>>> from django.http import HttpResponse


>>> response = HttpResponse(" Réponse …..")

>>> response2 = HttpResponse()


>>> response2.write("<p><h1>Bonjour M2-GL</h1></p>")
>>> response2.write("<p><h1>Bienvenue !</h1></p>")

Ces exemples de réponses seront exploitées après dans ce cours


185
Framework Django
Écriture d’une première vue:
Écrivons la première vue. Ouvrez le fichier polls/views.py et
placez-y le code Python suivant :
from django.http import HttpResponse
def index(request):
return HttpResponse("Bonjour, vous êtes sur polls index.")

C’est la vue Django la plus simple possible. Pour


appeler cette vue, il s’agit de l’associer à une URL,
et pour cela nous avons besoin d’un URLconf
(URL config).

Pour créer un URLconf dans le répertoire polls,


créez un fichier nommé urls.py. Votre répertoire
d’application devrait maintenant ressembler à
ceci : 186
Framework Django

Notez bien qu’on a deux fichiers qui


portent le nom: urls.py

1) urls.py dans le répertoire racine du


projet mysite

2) urls.py dans le répertoire de notre


application polls

187
Framework Django
Dans le fichier polls/urls.py, insérez le code suivant :
from django.urls import path
from . import views
urlpatterns = [ path('', views.index, name='index'),]
- Le paramètre path: route: une chaîne contenant un motif d’URL.
- Le paramètre view: fonction de vue
-Le paramètre name: Les espaces de noms d’URL permettent la
résolution inverse de motifs d’URL nommés de manière unique (voir
Résolution inversée des URL à la diapositive 233 de ce cours).

Une URL est une adresse web. Elle est visible dans la barre d'adresse de
votre navigateur. (127.0.0.1:8000 est aussi une URL ….)

Chaque page internet a besoin de sa propre URL. C'est grâce à cela que
votre application sait ce qu'elle doit afficher à un utilisateur qui ouvre cette
URL.

188
Framework Django
L’étape suivante est de faire pointer la configuration d’URL racine
vers le module polls.urls. Dans mysite/mysite/urls.py, ajoutez une
importation django.urls.include et insérez un appel include() dans la
liste urlpatterns, ce qui donnera :

mysite/mysite/urls.py:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [ ……………………………………..,
path('admin/', admin.site.urls),]

189
Framework Django
La fonction include() permet de référencer d’autres configurations
d’URL. Quand Django rencontre un include(), il tronque le bout
d’URL qui correspondait jusque là et passe la chaîne de caractères
restante à la configuration d’URL incluse pour continuer le
traitement.
L’idée derrière include() est de faciliter la connexion d’URL. Comme
l’application de sondages possède son propre URLconf
(polls/urls.py), ses URL peuvent être injectés sous « /polls/ », sous
« /fun_polls/ » ou sous « /content/polls/ » ou tout autre chemin
racine sans que cela change quoi que ce soit au fonctionnement de
l’application.
Quand utiliser include() ?
Il faut toujours utiliser include() lorsque l’on veut inclure d’autres
motifs d’URL. admin.site.urls est la seule exception à cette règle.

190
Framework Django
Vous avez maintenant relié une vue index dans la configuration d’URL.

Vérifiez qu’elle fonctionne avec la commande suivante :

..\> py manage.py runserver

Ouvrez http://localhost:8000/polls/ dans votre navigateur et vous


devriez voir le texte « Bonjour, vous êtes sur polls index. » qui a été
défini dans la vue index. (tester aussi: http://localhost:8000/sondage/)
➔ Tester la réponse 2 de la diapositive 168.

Page non trouvée ?


Si vous obtenez une page d’erreur ici, vérifiez que vous accédez bien
à http://localhost:8000/polls/ et non pas http://localhost:8000/

191
Gabarits
• Par sa nature liée au Web, Django a besoin d’un procédé de
génération dynamique de HTML. L’approche la plus couramment
utilisée est de se baser sur des gabarits. Un gabarit contient la
partie statique du résultat HTML souhaité ainsi qu’une certaine
syntaxe particulière définissant comment insérer le contenu
dynamique.
• Un gabarit Django est un document texte ou une chaîne Python,
balisés à l’aide du langage de gabarit de Django. Certaines
structures sont reconnues et interprétées par le moteur de
gabarit. Les principales sont les variables et les balises.
• Un gabarit est produit avec un contexte. Le processus de
production remplace les variables par leurs valeurs qui sont
cherchées dans le contexte, et il exécute les balises. Tout le reste
est affiché tel quel.
192
Gabarits
Les variables
Une variable affiche une valeur à partir du contexte, qui est un objet
de type dictionnaire faisant correspondre des clés à des valeurs.

Dans mysite/polls/view insérer la vue suivante:


from django.template import loader
from django.shortcuts import render
from django.http import HttpResponse

def ViewGabarit1(request):
ch="Bonjour mes chers"
I=2020
R=3.14
template = loader.get_template('polls/gabarit1.html')
context = {'MaChaine':ch, 'MonInt':I, 'MonRéel':R }
return HttpResponse(template.render(context,request))
193
Gabarits
Tout d’abord, créez un répertoire nommé templates dans votre
répertoire polls. C’est là que Django recherche les gabarits.

Le paramètre TEMPLATES de votre projet indique comment Django va


charger et produire les gabarits. Le fichier de réglages par défaut
configure un moteur DjangoTemplates dont l’option APP_DIRS est définie
à True. Par convention, DjangoTemplates recherche un sous-répertoire
« templates » dans chaque application figurant dans INSTALLED_APPS.

Dans le répertoire templates que vous venez de créer, créez un autre


répertoire nommé polls dans lequel vous allez placer vos fichiers de
gabarits (*.html). Autrement dit, le chemin de votre gabarit doit être :
polls/templates/polls/votreGabarit.html.

Très Important: avant de continuer, ajouter votre application polls


à INSTALLED_APPS en ajoutant la ligne: 'polls.apps.PollsConfig', dans
la liste INSTALLED_APPS du fichier mysite/mysite/setting.py.
194
Gabarits
Les variables
Créer le fichier html : mysite\polls\templates\polls \
gabarit1.html et y insérer le gabarit suivant:
Les variables sont entourées par {{ et }} comme ci-dessous :

<h3> <p style="color:#FF0000";>Je vous réponds en utilisant le


gabarit "gabarit1.html" </p></h3>

A partir de la vue <strong>ViewGabarit1</strong> j'ai récupéré les


données suivantes:<br>

une chaine de caractères: {{MaChaine}} <br>


<!—retour à la ligne-->
un nombre entier: {{MonInt}} <br>
un nombre réel: {{MonRéel}}
195
Gabarits
Les variables
Allez à polls/urls.py et ajouter l’ url suivant dans la liste urlpatterns :
urlpatterns = [
path('testGabarit1.html', views.ViewGabarit1),
……………]

Maintenant Django, va comprendre que lorsque vous demandez


la page 'testGabarit1.html', il va appeler la vue
views.ViewGabarit1 qui va répondre en renvoyant les données
des variables 'MaChaine', 'MonInt', 'MonRéel':R en se basant
sur le gabarit mysite\polls\templates\polls\gabarit1.html

196
Gabarits
Les variables
Démarrer le serveur et allez à :
http://127.0.0.1:8000/polls/testGabarit1.html

197
Gabarits
Les balises
Les balises permettent d’appliquer une logique arbitraire dans le
processus de rendu.
Par exemple, une balise peut produire du contenu, servir de
structure de contrôle telle qu’une instruction « if » ou une boucle
« for », extraire du contenu d’une base de données ou même de
donner accès à d’autres balises de gabarit.
Les balises sont entourées par {% et %}, comme ceci :

{% if MaChaine %} Votre chaine est: {{MaChaine }}.{% endif %}

{% for e in DictionnaireDesVentes.items %}
<li> Le vendeur: {{ e.0 }} a vendu {{ e.1 }} produits</li>
<!--tester e[0]-->
{% endfor %}
198
Gabarits
Les variables

Dans mysite/polls/view insérer la vue suivante:

def ViewGabarit2(request):

L=[2020, "Génie Logiciel", "CIGMA", "Master-M2"]


template = loader.get_template('polls/gabarit2.html')
context = {'ifosGLM2':L }
return HttpResponse(template.render(context,request))

199
Gabarits
Créer le fichier html : mysite\polls\templates\polls \ gabarit2.html et
y insérer le gabarit suivant:
<h3> <p style="color:#FF0000";>Je vous réponds en utilisant
le gabarit "C:\______________python cigma 20-
21\mysite\polls\templates\polls\gabarit2.html" </p></h3>

<ul> {% for e in ifosGLM2%}


<li> {{ e }} </li>
{% endfor %} </ul>

{% if ifosGLM2%}
<h3>Le nombre d'éléments dans la liste ifosGLM2:
{{ ifosGLM2|length }} </h3>
{% else %}
La liste ifosGLM2 donnée par la vue est vide
{% endif %} 200
Gabarits
Les variables
Allez à polls/urls.py et ajouter l’ url suivant dans la liste urlpatterns :
urlpatterns = [
path('testGabarit2.html', views.ViewGabarit2),
……………]

Maintenant Django, va comprendre que lorsque vous demandez


la page 'testGabarit2.html', il va appeler la vue
views.ViewGabarit2 qui va répondre en renvoyant les données
de la variable 'ifosGLM2’ en se basant sur le gabarit
mysite\polls\templates\polls\gabarit2.html

201
Gabarits
Démarrer le serveur et allez à :
http://127.0.0.1:8000/polls/testGabarit2.html

202
Gabarits
La consultation de dictionnaire, d’attribut et d’indice
de liste (ou chaine ou tuple) est implémentée par
une notation pointée :

{{ my_dict.key }}
{{ my_object.attribute }}
{{ my_list.0 }}

203
Gabarits
Les variables

Dans mysite/polls/view insérer la vue suivante:

def ViewGabarit3(request):

ventes = {"Amine": 14, "Rachid": 19, "Hamza": 15,


"Taha": 21, "Aya": 17}
template = loader.get_template('polls/gabarit3.html')
context = {'DictionnaireDesVentes':ventes }
return HttpResponse(template.render(context,request))

204
Gabarits
Créer le fichier html : mysite\polls\templates\polls \ gabarit3.html et
y insérer le gabarit suivant:

<h3> <p style="color:#FF0000";>Je vous réponds en utilisant le


gabarit "C:\______________python cigma 20-
21\mysite\polls\templates\polls\gabarit3.html" </p></h3>

<ol>
{% for e in DictionnaireDesVentes.items %}
<li> Le vendeur: {{ e.0 }} a vendu {{ e.1 }} produits</li>
<!--tester e[0]-->
{% endfor %}
</ol>

205
Gabarits
Les variables
Allez à polls/urls.py et ajouter l’ url suivant dans la liste urlpatterns :
urlpatterns = [
path('testGabarit3.html', views.ViewGabarit3),
……………]

Maintenant Django, va comprendre que lorsque vous demandez


la page 'testGabarit3.html', il va appeler la vue
views.ViewGabarit3 qui va répondre en renvoyant les données
de la variable ‘DictionnaireDesVentes’ en se basant sur le
gabarit mysite\polls\templates\polls\gabarit3.html

206
Gabarits
Démarrer le serveur et allez à :
http://127.0.0.1:8000/polls/testGabarit3.html

207
Gabarits
Dans mysite/polls/view insérer la vue suivante:
def ViewGabarit4(request):

L=[100,200,300,400,500]
ch='Bonjour'
ventes = {"Amine": 14, "Rachid": 19, "Hamza": 15,
"Taha": 21, "Aya": 17}
template = loader.get_template('polls/gabarit4.html')
context = {'DictionnaireDesVentes':ventes, 'maListe':L,
'maChaine':ch }
return HttpResponse(template.render(context,request))
208
Gabarits
Créer le fichier html : mysite\polls\templates\polls \ gabarit4.html et
y insérer le gabarit suivant:

<h3> <p style="color:#FF0000";>Je vous réponds en utilisant le


gabarit "C:\______________python cigma 20-
21\mysite\polls\templates\polls\gabarit4.html" </p></h3>

{{DictionnaireDesVentes.keys}}<br>
{{maListe.0}}<br>
{{maListe.4}}<br> <!--tester 2:4-->
{{maChaine.3}}
<!--maChaine.replace('o','a')-->
{#ceci est un commentaire sur le gabarit 4#}

209
Gabarits
Les variables
Allez à polls/urls.py et ajouter l’ url suivant dans la liste urlpatterns :
urlpatterns = [
path('testGabarit4.html', views.ViewGabarit4),
……………]

Maintenant Django, va comprendre que lorsque vous demandez


la page 'testGabarit4.html', il va appeler la vue
views.ViewGabarit4 qui va répondre en renvoyant les données
des variables ‘DictionnaireDesVentes’ 'maListe', 'maChaine’
en se basant sur le gabarit
mysite\polls\templates\polls\gabarit4.html

210
Gabarits
Démarrer le serveur et allez à :
http://127.0.0.1:8000/polls/testGabarit4.html

211
Gabarits
Les filtres:
Les filtres transforment les valeurs de variables et les
paramètres de balises.
Ils ressemblent à ceci :
{{ liste | length }}
{{ chaine|upper }}
{{maChaine|add:" mes amis"}}
…..

212
Gabarits
Dans mysite/polls/view insérer la vue suivante:
import datetime
def ViewGabarit5(request):
L=[100,200,300,400,500]
ch='Bonjour'
Datecomplete=datetime.datetime.now()
Annee = Datecomplete.year
ventes = {"Amine": 14, "Rachid": 19, "Hamza": 15, "Taha": 21, "Aya": 17}
template = loader.get_template('polls/gabarit5.html')
context = {'DictionnaireDesVentes':ventes, 'maListe':L, 'maChaine':ch,
'DateActuelle':Datecomplete, 'Annee':Annee}
return HttpResponse(template.render(context,request))
213
Gabarits
Créer le fichier html : mysite\polls\templates\polls \ gabarit5.html et y
insérer le gabarit suivant:

<h3> <p style="color:#FF0000";>Je vous réponds en utilisant le


gabarit: gabarit5.html </p></h3>
{{DictionnaireDesVentes.keys|length}}<br>
{{maChaine|upper}} ,
{{maChaine|lower}} <br> <!-- tester: maChaine|swapcase-->
{{ DictionnaireDesVentes.values}} <br>
<!-- tester: DictionnaireDesVentes.values.0|add:1000-->
{{maListe.0|add:40}} <br>
{{maChaine|add:" mes amis"}}<br>
Nous somme le, {{DateActuelle}}<br>
Nous somme le, {{DateActuelle|date:"D d M Y s w W"}} <br>
Bonne année {{Annee|add:1}}

214
Gabarits
Les variables
Allez à polls/urls.py et ajouter l’ url suivant dans la liste urlpatterns :
urlpatterns = [
path('testGabarit5.html', views.ViewGabarit5),
……………]

Maintenant Django, va comprendre que lorsque vous demandez


la page 'testGabarit5.html', il va appeler la vue
views.ViewGabarit5 qui va répondre en renvoyant les données
des différentes variables en se basant sur le gabarit
mysite\polls\templates\polls\gabarit5.html

215
Gabarits
Démarrer le serveur et allez à :
http://127.0.0.1:8000/polls/testGabarit5.html

216
Gabarits
Les moteurs de gabarit sont disponibles dans django.template.engines:
Dans mysite/polls/view insérer la vue suivante:
from django.template import engines
django_engine = engines['django']

Utilisation des gabarits sans la


def ViewGabarit6(request): création des fichiers gabarits.html
ch='Bonjour'
Datecomplete=datetime.datetime.now()
context = {'maChaine':ch,'DateActuelle':Datecomplete}
template = django_engine.from_string("{{maChaine|add:', '}}
Nous somme le:{{ DateActuelle }}!")
return HttpResponse(template.render(context,request))

217
Gabarits

Remarquez l’absence du ficher gabarit6.html dans cet


exemple.

Créer le fichier html : mysite\polls\templates\polls \ gabarit6.html


et y insérer le gabarit suivant:

218
Gabarits
Les variables

Allez à polls/urls.py et ajouter l’ url suivant dans la liste urlpatterns :


urlpatterns = [
path('testGabarit6.html', views.ViewGabarit6),
……………]

Maintenant Django, va comprendre que lorsque vous demandez la page


'testGabarit6.html', il va appeler la vue views.ViewGabarit6 qui va
répondre en renvoyant les données des différentes variables en se basant
sur un gabarit défini dans la vue: Views.ViewGabarit6.

219
Gabarits
Démarrer le serveur et allez à :
http://127.0.0.1:8000/polls/testGabarit6.html

220
Les URLs
Distribution des URL:
Une organisation propre et élégante des URL est un aspect
important dans une application Web de qualité. Django vous laisse
organiser vos URL comme bon vous semble, sans restriction
imposée par le système.

Pour organiser les URL d’une application, il s’agit de créer un


module Python appelé communément URLconf (configuration
d’URL). Le code de ce module est en Python pur et est une
correspondance entre expressions de chemins d’URL et fonctions
Python (vos vues).
221
Les URLs
Processus de traitement des requêtes par Django
Quand un utilisateur accède à une page de votre site Django, voici
l’algorithme que le système suit pour déterminer quel code python
doit être exécuté :

1) Django identifie le module URLconf racine à utiliser. Par défaut, c’est la


valeur attribuée au réglage ROOT_URLCONF défini dans notre cas
dans mysite/mysite/setting.py (par ROOT_URLCONF =
'mysite.urls’)

2) Django charge ce module Python (ici: 'mysite.urls’) et cherche la


variable urlpatterns. Ce devrait être
une séquence d’instances django.urls.path() ou django.urls.re_path()
(ici: re c’est pour les expression régulières).

3) Django parcourt chaque motif d’URL dans l’ordre et s’arrête dès qu’il
trouve la première correspondance de path_info avec l’URL
demandée.
222
Les URLs
Processus de traitement des requêtes par Django
4) Une fois qu’un des motifs d’URL correspond, Django importe et appelle
la vue correspondante, qui est une fonction Python. La vue se voit
passer les paramètres suivants :
• Une instance HttpRequest.
• Si le motif d’URL correspondant ne contient pas de groupes
nommés, les correspondances de l’expression régulière sont
transmises comme paramètres positionnels.

4) Si aucun motif d’URL ne correspond, ou si une exception est levée


durant ce processus, Django appelle une vue d’erreur appropriée.
Ces étapes sont expliquées dans l’exemple ci-dessous:
223
Les URLs
Exemple: Processus de traitement des URLs par Django
Voici un exemple d’URLconf : Views.py:
def year_archive(year):
…………
def month_archive(year,month):
from django.urls import path
…………
def article_detail(year,month,slug):
from . import views
…………
def V1(x):
urlpatterns = [
……………………..
path('articles/2003/', views.special_case_2003),

path('articles/<int:year>/', views.year_archive),
path('articles/<int:year>/<int:month>/', views.month_archive),
path('articles/<int:year>/<int:month>/<slug:slug>/’, views.article_detail),

path('questions/<path:chemin>/', view.V1()), ] 224


Les URLs
Exemple: Processus de traitement des URLs par Django
Notes :
• Pour capturer une valeur contenue dans l’URL, utilisez des chevrons.

• Les valeurs capturées peuvent inclure un convertisseur de type. Par


exemple, <int:year> va capturer un paramètre nombre entier. Si aucun
convertisseur n’est donné, l’expression capture n’importe quelle chaîne de
caractères, à l’exception du caractère /.

• Inutile de préfixer vos URL d’une barre oblique, elles en ont toutes une.

Par exemple, écrivez path('articles/<…’) et non path(‘/articles/<…’).

225
Les URLs
Exemple: Processus de traitement des URLs par Django
•Une requête vers /articles/2005/03/ correspondrait à la troisième entrée
dans la liste. Django appellerait la fonction:
views.month_archive(request, year=2005, month=3).

•/articles/2003/ correspondrait au premier motif de la liste, et non le


deuxième, car les motifs sont évalués dans l’ordre, et le premier est le
premier à correspondre. Libre à vous d’utiliser l’ordre de définition pour
traiter des cas spéciaux comme ici. Ici, Django appellerait la
fonction views.special_case_2003(request).

•/articles/2003 ne correspondrait à aucun motif, car chaque motif nécessite


que l’URL se termine par une barre oblique /.

•/articles/2003/03/construire-un-site-django/ correspondrait à l’avant dernier


motif. Django appellerait la fonction:
views.article_detail(request, year=2003, month=3, slug="construire-un-site-
django"). 226
Les URLs
Convertisseurs de chemin: Les convertisseurs de chemin suivants sont
disponibles par défaut :

• str - correspond à n’importe quelle chaîne non vide, à l’exception du


séparateur de chemin, '/'. C’est ce qui est utilisé par défaut si aucun
convertisseur n’est indiqué dans l’expression.
•int - correspond tout nombre entier positif. Renvoie le type int.
•slug - correspond à toute chaîne composée de lettres ou chiffres ASCII, du
trait d’union ou du caractère "_". Exemple:"construire-votre-1er-site-django".

•uuid - correspond à un identifiant UUID. tirets inclus et les lettres en


minuscules. Par exemple: 075194d3-6885-417e-a8a8-6c931e272f00.
Renvoie une instance UUID.

•path - correspond à n’importe quelle chaîne non vide, y compris le


séparateur de chemin, '/'. Cela permet de correspondre à un chemin d’URL
complet au lieu d’un segment de chemin d’URL comme avec str.

227
Gabarits
Cas des listes contenant des listes …

Dans mysite/polls/view insérer la vue suivante:


def ViewGabarit8(request, debut, fin, pas,ch):

L=[100,200,300,400,500, 1000, 2000, [250,600,800,300,600] ]


template = loader.get_template('polls/gabarit8.html')
positionMot="Bonjour Mes Chers. Bonne continuation avec
Django!".index(ch)
context = {'ElementsDeListe':L[debut:fin+1:pas], 'pos':positionMot}
return HttpResponse(template.render(context,request))

228
Gabarits
Créer le fichier html : mysite\polls\templates\polls \ gabarit8.html et y
insérer le gabarit suivant:

<h3> <p style="color:#FF0000";>Je vous réponds en utilisant le


gabarit: gabarit8.html </p></h3>

Les éléments de la liste qui correspondent à votre requête sont:<br>


{{ElementsDeListe}}<br>

Si votre début est 1, alors le premier élément de la sous liste


est:{{ElementsDeListe.6.0}}<br>
Votre mot se trouve dans la position: {{pos}} de la chaine:<br>
"Bonjour Mes Chers. Bonne continuation avec Django!"

229
Gabarits
Les variables
Allez à polls/urls.py et ajouter l’ url suivant dans la liste urlpatterns :
urlpatterns = [
path('<int:debut>/<int:fin>/<int:pas>/<str:ch>/', views.ViewGabarit8),
……………]

Maintenant Django, va comprendre que lorsque vous demandez


la page 'testGabarit8.html', il va appeler la vue
views.ViewGabarit8 qui va recevoir les paramètres:
debut, fin, pas, ch et va répondre en renvoyant les données de:
ViewGabarit8(debut, fin, pas, ch) des variables:
ElementsDeListe et pos en se basant sur le gabarit
mysite\polls\templates\polls\gabarit8.html

230
Gabarits
Démarrer le serveur et allez à :
http://127.0.0.1:8000/polls/1/10/1/avec/

ViewGabarit8(debut, fin, pas, ch)

231
Les expressions régulières
Le module re a été spécialement conçu pour travailler avec les expressions
régulières (Regular Expressions).

La fonction search attend deux paramètres obligatoires : l'expression


régulière, sous la forme d'une chaîne, et la chaîne de caractères dans
laquelle on recherche cette expression. Si l'expression est trouvée,
la fonction renvoie un objet symbolisant l'expression recherchée.
Sinon, elle renvoie None.

232
Les expressions régulières

233
Les expressions régulières
Pour rechercher au début de la chaîne: le signe ^.
Pour matérialiser la fin de la chaîne, vous utiliserez le signe $.

chat*:
Nous avons rajouté une astérisque (*) après le caractère t de chat.
Cela signifie que notre lettre t pourra se retrouver 0, 1, 2, … fois
dans notre chaîne. Autrement dit, notre expression chat* sera
trouvée dans les chaînes suivantes : 'chat', 'chaton', 'chateau',
'herbe à chat', 'chapeau', 'chatterton', 'chattttttttt'

import re
print(re.match('chat*','chapeau')!=None) ➔ True
print(re.match('chat*','chatterton')!=None) ➔ True
print(re.match('chat*','chameau')!=None) ➔ True

bat*e:
Cette expression est trouvée dans les chaînes suivantes :
'bateau', 'batteur' et 'joan baez'.
234
Les expressions régulières

235
Les expressions régulières: Les classes de caractères
•Vous pouvez préciser entre crochets plusieurs caractères ou classes de

caractères. Par exemple, si vous écrivez [abcd], cela signifie : l'une des

lettres parmi a, b, c et d.

•Pour exprimer des classes, vous pouvez utiliser le tiret - entre deux lettres

par exemple, l'expression [A-Z] signifie « une lettre majuscule ». Vous

pouvez préciser plusieurs classes ou possibilités dans votre expression.:

Ainsi, l'expression [A-Za-z0-9] signifie « une lettre, majuscule ou minuscule,

ou un chiffre ».

•Si vous voulez par exemple rechercher 5 lettres majuscules qui se suivent

dans une chaîne, votre expression sera [A-Z]{5}.


236
Les expressions régulières: Les classes de caractères
•Si vous voulez appliquer ce contrôle d'occurence à plusieurs caractères, vous
allez placer ces caractères entre parenthèses. (abc){2,5} cette expression
sera vérifiée pour les chaînes contenant la séquence ' abc‘ répétée entre deux
et cinq fois. Les séquences 'abc' doivent se suivre naturellement.

•la barre verticale | représente des choix multiples dans un sous-motif.

L'expression Duran[dt] peut aussi s'écrire (Durand | Durant).


•On peut utiliser l'expression (lu|ma|me|je|ve|sa|di) dans l'écriture
d'une date par exemple.

237
Les expressions régulières
En plaçant un r avant le délimiteur qui ouvre notre chaîne, tous les
caractères anti-slash qu'elle contient sont échappés.
print(re.match(r'Fromage\s[a-zA-Z]{0,}', 'Fromage amk')!=None)
➔True
X='FromageLavacheQuiRit‘
print(re.match(r'Fromage\s[a-zA-Z]{0,}',X)!=None)
False
\ : symbole d'échappement ;
\d : classe des nombres entiers ;
\s : classe des caractères d'espacement ;
\w : classe des caractères alphanumériques ;
\D : négation de la classe \d ;
\S : négation de la classe \s ;
\W : négation de la classe \w ; 238
Les expressions régulières
. désigne tout caractère non spécial quel qu’il soit
\d désigne tout chiffre, est équivalent à [0-9]
\D désigne tout caractère différent d’un chiffre, est équivalent à [^0-9]
\s désigne tout espace ou caractère approché, est équivalent
à [\; \t\n\r\f\v]. Ces caractères sont spéciaux, les plus utilisés sont \t qui
est une tabulation, \n qui est une fin de ligne et qui \r qui est un retour
à la ligne.
\S désigne tout caractère différent d’un espace, est équivalent
à [^ \t\n\r\f\v]
\w désigne tout lettre ou chiffre, est équivalent à [a-zA-Z0-9\_]
\W désigne tout caractère différent d’une lettre ou d’un chiffre, est
équivalent à [^a-zA-Z0-9\_]
^ désigne le début d’un mot sauf s’il est placé entre crochets
$ désigne la fin d’un mot sauf s’il est placé entre crochets

239
Les expressions régulières
telephone='0645124511'
if re.match('^0[5-6]([.-]?[0-9]{2}){4}$',telephone):
print("oui, numero de telephone valide")
else:
print("Non ! numero de telephone invalide")

mails= ["olivier@mailbidon.com", "olivier@mailbidon.ma",


"8@mailbidon.com", "@mailbidon.com","olivier@mailbidon"]

expression="^[a-zA-Z][A-Za-z0-9._-]+@[A-Za-z._-]+.[(com|fr|ma)]$"
for mail in mails:
if re.match(expression,mail)!=None:
print ("Ce mail : " +mail + " est valide")
else:
print ("Ce mail : " +mail + " est non valide")
oui, numero de telephone valide
Ce mail : olivier@mailbidon.com est valide
Ce mail : olivier@mailbidon.ma est valide
Erreur ce mail : 8@mailbidon.com est non valide
Erreur ce mail : @mailbidon.com est non valide
Erreur ce mail : olivier@mailbidon est non valide
Les URLs
Utilisation d’expressions régulières: Si la syntaxe des chemins et
convertisseurs n’est pas suffisante pour définir vos motifs d’URL, vous
pouvez également utiliser des expressions régulières. Pour ce faire,
utilisez re_path() à la place de path().

La syntaxe des expressions régulières en Python pour les groupes de

captures nommés est (?P<nom>motif), où nom est le nom du groupe


et motif est le motif correspondant.
Exemple:

re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive)
Voici de nouveau l’exemple de configuration d’URL écrit en utilisant des
expressions régulières :
241
Les URLs
from django.urls import path, re_path ; from . import views

urlpatterns = [ path('articles/2003/', views.special_case_2003),

re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$',
views.month_archive),

re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-]+)/$',
views.article_detail), ]

Cela fait plus ou moins la même chose que l’exemple précédent, sauf que :
➢L’URL précis qui va correspondre sera légèrement plus contraint. Par
exemple, l’année 10000 ne correspondra plus puisque les nombres entiers
des années sont limités à exactement quatre chiffres.
➢Chaque paramètre capturé est envoyé à la vue en tant que chaîne de
caractères, peu importe la correspondance qu’effectue l’expression régulière
(ce problème peut être géré en faisant des conversion dans les vues). 242
Les URLs
from django.urls import path, re_path ; from . import views
urlpatterns = [ path('articles/2003/', views.special_case_2003),
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
…………..]

➢ En plus de la syntaxe des groupes nommés, par ex. (?P<year>[0-9]{4}), il


est aussi possible d’utiliser des groupes non nommés plus courts, par
exemple: ([0-9]{4}).

➢ Cette utilisation n’est pas particulièrement recommandée car le risque est


plus grand d’introduire accidentellement des erreurs entre la signification
espérée d’une correspondance et les paramètres de la vue.

➢Dans les deux cas, il est recommandé d’employer un seul style dans une
expression donnée. Lorsque les styles sont mélangés, tout groupe non
nommé est ignoré et seuls les groupes nommés sont transmis à la fonction
de vue.
243
Gabarits
Dans mysite/polls/view insérer la vue suivante:
from datetime import *
def ViewGabarit9(request,year, month, day):
d = date(int(year), int(month), int(day))
L=["Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi","Dimanche"]
ch="C'est un "+str(L[d.weekday()])
template = loader.get_template('polls/gabarit9.html')
context = {'reponse':ch}
return HttpResponse(template.render(context,request))

244
Gabarits
Créer le fichier html : mysite\polls\templates\polls \ gabarit9.html et y
insérer le gabarit suivant:

<h3> <p style="color:#FF0000";>


Je vous réponds en utilisant le gabarit: gabarit9.html </p></h3>

{{reponse}}

245
Gabarits
Les variables
Allez à polls/urls.py et ajouter l’ url suivant dans la liste urlpatterns :
from django.urls import path, re_path

urlpatterns = [
re_path(r'^date/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/
(?P<day>[0-9]{2})/$', views.ViewGabarit9), ……………]

Maintenant Django, va comprendre que lorsque vous demandez


par exemple la page: http://127.0.0.1:8000/polls/date/2020/12/05/,
il va appeler la vue views.ViewGabarit9 qui va recevoir les
paramètres: year, month, day et va répondre en renvoyant les
données de: ViewGabarit9(year, month, day) de la variable:
reponse en se basant sur le gabarit
mysite\polls\templates\polls\gabarit9.html 246
Gabarits
Démarrer le serveur et allez à :
http://127.0.0.1:8000/polls/date/2020/12/05/

ViewGabarit9(year, month, day)

247
Gabarits
Démarrer le serveur et allez à :
http://127.0.0.1:8000/polls/date/20200/12/05/

ViewGabarit9(year, month, day)

248
Les URLs
Valeurs par défaut des paramètres de vue:
Une astuce pratique est de définir des valeurs par défaut pour les paramètres de
vos vues. Voici un exemple de configuration d’URL et de vue :

# URLconf
from django.urls import path
from . import views
urlpatterns = [ path('blog/', views.page),
path('blog/page<int:num>/', views.page), ]
# View (in blog/views.py)
def page(request, num=1):
. ...

Dans l’exemple ci-dessus, les deux motifs d’URL pointent vers la même
vue: views.page mais le premier motif ne capture aucun élément de l’URL. Si le
premier motif correspond, la fonction page() utilisera la valeur par défaut de son
paramètre num=1. Si le second motif correspond, page() utilisera la valeur capturée.
249
Les URLs
Résolution inversée des URL:
Django permet de travailler dans les deux sens suivants:

➢ À partir d’une URL demandée par un utilisateur/navigateur, il appelle la vue


concernée en lui passant tous les paramètres dont elle a besoin en extrayant
les valeurs telles quelles de l’URL.
➢ À partir de l’identification de la vue Django concernée ainsi que les valeurs
des paramètres qui lui seraient passés, il renvoie l’URL associée.

Le premier sens correspond à l’utilisation dont nous avons parlé dans les
sections précédentes. Le second correspond à ce qu’on appelle la résolution
inversée des URL, ou plus simplement l’inversion d’URL.

Django met à votre disposition des outils pour réaliser une inversion d’URL dans
les différentes couches où les URL sont nécessaires.

Dans le code Python : en utilisant la fonction reverse().

250
Les URLs
Exemples: Reprenons comme exemple cette ligne de configuration d’URL :

Dans mysite/polls/view insérer la vue suivante:


from django.utils import timezone
import datetime
def ViewGabarit7(request, N): # cette vue sera utilisée das la vue
# redirect_to_autre_URL de la diapo 255
now=datetime.datetime.now()
DateDecalee=now - datetime.timedelta(days=N)
template = loader.get_template('polls/gabarit7.html')
context =
{'valeurDeN':N,'DateAujourdhuit':now,'LaDateDecalee':DateDecalee}
return HttpResponse(template.render(context, request))
251
Les URLs
Créer le fichier html : mysite\polls\templates\polls \ gabarit7.html et
y insérer le gabarit suivant:

<h3> <p style="color:#FF0000";>


Je vous réponds en utilisant le gabarit: gabarit7.html</p></h3>

Nous somme le, {{DateAujourdhuit}}<br>

Avant {{valeurDeN}} jours, nous étions le:{{LaDateDecalee}}

252
Les URLs
Les variables
Allez à polls/urls.py et ajouter l’ url suivant dans la liste urlpatterns :
urlpatterns = [
path('<int:N>/', views.ViewGabarit7),
……………]

Maintenant Django, va comprendre que lorsque vous demandez


la page 'http://127.0.0.1:8000/polls/un nombre N/', il va
appeler la vue views.ViewGabarit7 qui va répondre en recevant
un entier N et renvoyant des données en se basant sur le
gabarit mysite\polls\templates\polls\gabarit7.html

253
Les URLs
Démarrer le serveur et allez à : http://127.0.0.1:8000/polls/2/

254
Les URLs
Allez à polls/urls.py et modifier l’ url pécédent dans la liste urlpatterns :
En ajoutant: 'name=URL_des_decalages‘
Et ajouter la ligne: path('ici/<int:X>/', views.redirect_to_autre_URL),
urlpatterns = [
path('<int:N>/', views.ViewGabarit7, name='URL_des_decalages'),
path('ici/<int:X>/', views.testredirect_to_autre_URL), ………]
Dans mysite/polls/view insérer la vue suivante:
from django.http import HttpResponseRedirect
from django.urls import reverse
def testredirect_to_autre_URL(request, X):
if X>1000: return HttpResponse ('vous avez saisi un grand entier ! ' )
else:
return HttpResponseRedirect(reverse('URL_des_decalages', args=(X,)))
255
Les URLs
Démarrer le serveur et allez à : http://127.0.0.1:8000/polls/ici/100/

http://127.0.0.1:8000/polls/ici/1000000/

256
Framework Django
Création d’une application Django pour les sondages
(polls) : Configuration de la base de données
Maintenant, ouvrez mysite/settings.py. C’est un module Python avec des
variables de module qui représentent des réglages de Django.

La configuration par défaut utilise SQLite.

ENGINE – Choisissez 'django.db.backends.sqlite3‘.

NAME – Le nom de votre base de données. Si vous utilisez SQLite, la

base de données sera un fichier sur votre ordinateur. Dans ce cas, NAME

doit être le chemin absolu complet de celui-ci, y compris le nom de fichier.

La valeur par défaut, os.path.join(BASE_DIR, 'db.sqlite3'), stocke ce

fichier dans le répertoire de votre projet. 257


Framework Django
Puisque vous êtes en train d’éditer mysite/settings.py,
définissez TIME_ZONE selon votre fuseau horaire.
Notez également le réglage INSTALLED_APPS au début du fichier. Cette variable
contient le nom des applications Django qui sont actives dans cette instance de
Django. Les applications peuvent être utilisées dans des projets différents, et vous
pouvez empaqueter et distribuer les vôtres pour que d’autres les utilisent dans
leurs projets.
Par défaut, INSTALLED_APPS contient les applications suivantes, qui sont toutes
contenues dans Django :
django.contrib.admin – Le site d’administration. Vous l’utiliserez très bientôt.
django.contrib.auth – Un système d’authentification.
django.contrib.contenttypes – Une structure pour les types de contenu (content
types).
django.contrib.sessions – Un cadre pour les sessions.
django.contrib.messages – Un cadre pour l’envoi de messages.
django.contrib.staticfiles – Une structure pour la prise en charge des fichiers
statiques.
Ces applications sont incluses par défaut par commodité parce que ce sont
les plus utilisées.
258
dans mysite/settings.py, définissez TIME_ZONE :
TIME_ZONE = 'Africa/Casablanca‘ #'UTC'
#TIME_ZONE = 'Europe/Istanbul‘’ et faite un test selon

def ViewGabarit5_v2(request):
from django.utils import timezone
d=timezone.now()
template = loader.get_template('polls/gabarit5_time_zone.html')
context = {'DateActuelle':d}
return HttpResponse(template.render(context,request))

Fichier gabarit5_time_zone.html: Nous somme le, {{DateActuelle}}

urls.py:
path('test_time_zone.html', views.ViewGabarit5_v2),

259
Framework Django
il nous faut créer les tables dans la base avant de pouvoir les
utiliser. Pour ce faire, lancez la commande suivante :

py manage.py migrate

La commande migrate examine le


réglage INSTALLED_APPS et crée les tables de base de
données nécessaires en fonction des réglages de base de
données dans votre fichier mysite/settings.py et des
migrations de base de données contenues dans l’application.
Vous verrez apparaître un message pour chaque migration
appliquée.

260
Framework Django
py manage.py migrate

261
Création d’une application Django pour les sondages (polls) : suite

Création des modèles:


Nous allons maintenant définir les modèles – essentiellement, le schéma de base de
données, avec quelques métadonnées supplémentaires.
Dans notre application de sondage:
Nous allons créer deux modèles : Question et Choice (choix).
Une Question possède une question et une date de mise en ligne.
Un choix a deux champs : le texte représentant le choix et le décompte des votes.
Chaque choix est associé à une Question (relation 1 à plusieurs).

ORM Django est l'acronyme anglais de object relational mapping ,


donc mapping d'objet relationnel en français. Un ORM est une
technique de programmation qui donne l' illusion de travailler avec une
base de données orientée objet . Pour résumer vous ne faites plus de
requetes SQL mais vous travaillez directement avec vos objets.
262
Framework Django
Ces concepts sont représentés par des classes Python. Éditez le
fichier polls/models.py de façon à ce qu’il ressemble à ceci :

from django.db import models

class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')

class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
# il ya aussi FloatField

263
Framework Django
Ici, chaque modèle est représenté par une classe qui hérite de:
django.db.models.Model. Chaque modèle possède des variables de classe,
chacune d’entre elles représentant un champ de la base de données.
Chaque champ est représenté par une instance d’une classe Field – par
exemple, CharField pour les champs de type caractère, et DateTimeField
pour les champs date et heure. Cela indique à Django le type de données
que contient chaque champ.
Le nom de chaque instance de Field (par exemple, question_text ou pub_date)
est le nom du champ en interne.
Notez que nous définissons une relation, en utilisant ForeignKey.
Cela indique à Django que chaque vote (Choice) n’est relié qu’à une seule
Question. Django propose tous les modèles classiques de relations :
plusieurs-à-un, plusieurs-à-plusieurs, un-à-un. 264
Framework Django
Activation des modèles:
Ce petit morceau de code décrivant les modèles fournit beaucoup
d’informations à Django. Cela lui permet de :
- Créer un schéma de base de données (instructions CREATE TABLE) pour
cette application.
- Créer une API Python d’accès aux bases de données pour accéder aux
objets Question et Choice (comme nous allons le voir dans la suite de ce
cours).
Mais il faut d’abord indiquer à notre projet que l’application de
sondages polls est installée.

265
Framework Django
Pour inclure l’application dans notre projet, nous avons besoin d’ajouter
une référence à sa classe de configuration dans le
réglage INSTALLED_APPS. La classe PollsConfig se trouve dans le
fichier polls/apps.py, ce qui signifie que son chemin pointé
est 'polls.apps.PollsConfig'. Modifiez le fichier mysite/settings.py et ajoutez
ce chemin pointé au réglage INSTALLED_APPS. Il doit ressembler à ceci :

monsite/settings.py

INSTALLED_APPS = [ 'polls.apps.PollsConfig', 'django.contrib.admin',


'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.staticfiles', ]

Maintenant, Django sait qu’il doit inclure l’application polls.

Remarque: Cette opération de configuration est déjà faite dans un TP antérieur


Framework Django
En exécutant makemigrations, vous indiquez à Django que vous avez
effectué des changements à vos modèles (dans ce cas, vous en avez
créé des modèles) et que vous aimeriez que ces changements soient
stockés sous forme de migration.
Les migrations sont le moyen utilisé par Django pour stocker les
modifications de vos modèles (et donc de votre schéma de base de
données), il s’agit de fichiers sur un disque. Vous pouvez consultez la
migration pour vos nouveaux modèles si vous le voulez ; il s’agit du
fichier polls/migrations/0001_initial.py.
py manage.py makemigrations polls

267
Framework Django
class Migration(migrations.Migration):
initial = True
fichier polls/migrations/0001_initial.py.
dependencies = [ ]
operations = [migrations.CreateModel(name='Question',
fields=[('id', models.AutoField(auto_created=True, primary_key=True,
serialize=False, verbose_name='ID')),
('question_text', models.CharField(max_length=200)),
('pub_date', models.DateTimeField(verbose_name='date
published')),
],
),
migrations.CreateModel( name='Choice',
fields=[ ('id', models.AutoField(auto_created=True, primary_key=True,
serialize=False, verbose_name='ID')),
('choice_text', models.CharField(max_length=200)),
('votes', models.IntegerField(default=0)),
('question',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
to='polls.question')),
],
),
]
268
Framework Django
Il existe une commande qui exécute les migrations et gère
automatiquement votre schéma de base de données, elle s’appelle migrate.
Nous y viendrons bientôt, mais tout d’abord, voyons les instructions SQL
que la migration produit. La commande sqlmigrate accepte des noms de
migrations et affiche le code SQL correspondant :

py manage.py sqlmigrate polls 0001

•Notez bien que: La commande sqlmigrate n’exécute pas réellement


la migration dans votre base de données - elle se contente de
l’afficher à l’écran de façon à vous permettre de voir le code SQL
que Django pense nécessaire (voir diapo précédente).

269
Framework Django

270
Framework Django
Notez les points suivants :
•Ce que vous verrez dépendra de la base de données que vous utilisez.
L’exemple ci-dessus est généré pour SQLite.

•Les noms de tables sont générés automatiquement en combinant le nom de


l’application (polls) et le nom du modèle en minuscules – question et choice
➔ (polls_question, polls_choice).

•Des clés primaires (ID) sont ajoutées automatiquement.

•Par convention, Django ajoute "_id" au nom de champ de la clé étrangère


(c’est le cas de question ➔ question_id dans choice): voir base de donnée

•La relation de clé étrangère: explicite par une contrainte FOREIGN KEY.

•Ce que vous voyez est adapté à la base de données que vous utilisez.
Ainsi, des champs spécifiques à celle-ci
comme integer primary key autoincrement (SQLite) sont gérés pour vous
automatiquement. Tout comme pour les guillemets autour des noms 271 de
champs (simples ou doubles).
Framework Django
Remarquez la création de la base db.sqlite3 dans votre dossier: mysite

272
Framework Django
vous pouvez aussi exécuter python manage.py check; cette commande vérifie la
conformité de votre projet sans appliquer de migration et sans toucher à la base de
données.
(venv) C:\______________python cigma 20-21\mysite>py manage.py check
System check identified no issues (0 silenced).

Maintenant, exécutez à nouveau la commande migrate pour créer les tables


des modèles dans votre base de données :

...\> py manage.py migrate


Operations to perform: Apply all migrations: admin, auth, contenttypes, polls,
sessions Running migrations: Rendering model states... DONE Applying
polls.0001_initial... OK

La commande migrate sélectionne toutes les migrations qui n’ont pas été appliquées
(Django garde la trace des migrations appliquées en utilisant une table spéciale dans
la base de données : django_migrations (voir diapositive précédente: l’avant dernière
table) puis les exécute dans la base de données, ce qui consiste essentiellement à
synchroniser les changements des modèles avec le schéma de la base de données.
273
Framework Django
Remarquez la modification de la base db.sqlite3 dans votre dossier:
mysite (ajout de deux tables)

274
Framework Django
Les migrations sont très puissantes et permettent de gérer les
changements de modèles dans le temps, au cours du développement d’un
projet, sans devoir supprimer la base de données ou ses tables et en
refaire de nouvelles. Une migration s’attache à mettre à jour la base de
données en live, sans perte de données.

Exemple d’application :
1) Modifiez les modèles (dans models.py).Par exemple, ajouter la ligne:
question_type = models.CharField(max_length=100, default='polytique') à
la classe Question.
2) Exécutez py manage.py makemigrations polls pour créer des
migrations correspondant à ces changements.
3) Exécutez py manage.py migrate pour appliquer ces modifications à la
base de données.
4) supprimer question_type = models.CharField(max_length=100,
default='polytique') de la classe Question et refaire les étapes 2) et 3).
Framework Django
(venv) C:\______________python cigma 20-21\mysite>py manage.py makemigrations polls
Migrations for 'polls':
polls\migrations\0002_question_question_type.py
- Add field question_type to question

(venv) C:\______________python cigma 20-21\mysite>py manage.py migrate


Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Applying polls.0002_question_question_type... OK

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(venv) C:\______________python cigma 20-21\mysite>py manage.py makemigrations polls


Migrations for 'polls':
polls\migrations\0003_remove_question_question_type.py
- Remove field question_type from question

(venv) C:\______________python cigma 20-21\mysite>py manage.py migrate


Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Applying polls.0003_remove_question_question_type... OK
Framework Django
Jouer avec l’interface de programmation (API)
Maintenant, utilisons un shell interactif Python pour jouer avec l’API que
Django met gratuitement à votre disposition. Pour lancer un shell Python,
utilisez cette commande : py manage.py shell
(pour le quitter après tapper exit() )

>>> from polls.models import Choice, Question


# No questions are in the system yet.
>>> Question.objects.all()
<QuerySet []>
# Create a new Question.
>>> from django.utils import timezone
>>> Q=Question(question_text="vous voulez enseignement à distance ou
présentiel?",pub_date=timezone.now())
# Save the object into the database. You have to call save() explicitly.
>>> Q.save() # Now it has an ID.
>>> Q.id
1 C’est comme l’instruction SQL insert into….
277
Framework Django
Vérifier l’ajout de cette question dans votre base de données !

278
Framework Django
Jouer avec l’interface de programmation (API)
# Access model field values via Python attributes.
>>> Q.question_text
'vous voulez enseignement à distance ou présentiel?'
>>> Q.pub_date
datetime.datetime(2020, 11, 11, 13, 25, 21, 852167, tzinfo=<UTC>)
>>> Q.question_text=Q.question_text+ "ou enseignement hybride ?"
>>> Q.save()
>>> Q.question_text
'vous voulez enseignement à distance ou présentiel?ou enseignement
hybride ?‘

# objects.all() displays all the questions in the database.


>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>

279
Framework Django
Jouer avec l’interface de programmation (API)

<Question: Question object (1)> n’est pas une représentation très


utile de cet objet. On va arranger cela en éditant le
modèle Question (dans le fichier polls/models.py) et en ajoutant une
méthode __str__() à Question et à Choice:

from django.db import models


class Question(models.Model):
# ...
def __str__(self): return self.question_text
class Choice(models.Model):
# ...
def __str__(self): return self.choice_text

280
Framework Django
Jouer avec l’interface de programmation (API)

Ajoutons aussi une méthode personnalisée à ce modèle


import datetime
import models from django.utils
from django.utils import timezone
class Question(models.Model):
# ...
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=7)

>>> timezone.now()
datetime.datetime(2020, 11, 11, 13, 53, 4, 556444, tzinfo=<UTC>)
>>> x=timezone.now() - datetime.timedelta(days=10)
>>> x
datetime.datetime(2020, 11, 1, 13, 53, 8, 102430, tzinfo=<UTC>)
281
Framework Django
Jouer avec l’interface de programmation (API)
Enregistrez ces modifications et retournons au shell interactif de Python
en exécutant à nouveau python manage.py shell ou bien Arrêter
(boutton 2: rouge) et reRun (Boutton 1) Shell Python

>>> from polls.models import Choice, Question


# Make sure our __str__() addition worked.

>>> Question.objects.all()
<QuerySet [<Question: vous voulez enseignement à
distance ou présentiel?ou enseignement hybride ?>]>

>>> Question.objects.filter(id=1)
<QuerySet [<Question: vous voulez enseignement à
distance ou présentiel?>]>

282
Framework Django
Jouer avec l’interface de programmation (API)

>>> Question.objects.filter(question_text__startswith='vous')
<QuerySet [<Question: vous voulez enseignement à distance ou
présentiel? ou enseignement hybride ?>]>

# Get the question that was published this year.


>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: vous voulez enseignement à distance ou présentiel?ou
enseignement hybride ?>
>>> Question.objects.get(id=2)
Traceback (most recent call last):
File "<input>", line 1, in <module>……………………………..
283
Framework Django
#The following is identical to Question.objects.get(id=1).
>>> Question.objects.get(pk=1)
<Question: vous voulez enseignement à distance ou présentiel?ou
enseignement hybride ?>
# Make sure our custom method worked.
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True
>>> q.choice_set.all() # afficher les choices liées à q
<QuerySet []>
>>> q.choice_set.create(choice_text='Présentiel', votes=10)
>>> q.choice_set.create(choice_text='Distantiel', votes=15)
>>> q.choice_set.create(choice_text='Hybride', votes=15)
>>> q.choice_set.all()
<QuerySet [<Choice: Présentiel>, <Choice: Distantiel>, <Choice: Hybride>]>
>>> q.choice_set.count()
3
>>> q.save() # voir la table choice sous sqlitestudio par exemple: les 3 choix sont ajoutés
284
Framework Django
# Let's delete one of the choices. Use delete() for that.
>>> c = q.choice_set.filter(choice_text__startswith='Hyb')
>>> c.delete()
>>> c.delete()
Ajout d’autres questions
>>> from polls.models import Choice, Question
>>> Q=Question(question_text="Vous voulez prendre un café ou non
?",pub_date="2020-11-11 13:27:21.852167"); Q.save();
>>> Q=Question(question_text="Vous préférer cours le matin ou l'aprés midi ?
",pub_date="2020-11-11 18:27:21.852167"); Q.save();
>>> Q=Question(question_text="Vous êtes pour l'invertissement dans des projets
commerciaux ou des projets éducatifs ?",pub_date="2020-11-23
13:27:21.852167"); Q.save();
>>> Q=Question(question_text="Vous préférer faire la médecine ou l'ingénierie
?",pub_date="2020-11-15 18:27:21.852167"); Q.save();
>>> Q=Question(question_text="Vous préférez les écoles nationales ou celles de
missions ?",pub_date="2020-12-11 18:27:21.852167"); Q.save();

285
Framework Django
Ajout d’autres choix
>>> q = Question.objects.get(pk=1)
>>> q.choice_set.create(choice_text='Hybride', votes=15)

>>> q.choice_set.all()
<QuerySet [<Choice: Présentiel>, <Choice: Distantiel>, <Choice: Hybride>]>

>>> choix2=Choice(question_id = 2,choice_text = 'oui, je veux un café', votes=6);


choix2.save();

>>> choix2=Choice(question_id = 2,choice_text = 'non, je ne veux pas un café',


votes=1); choix2.save();

>>> choix3=Choice(question_id = 3,choice_text = 'matin', votes=20); choix3.save();

>>> choix3=Choice(question_id = 3,choice_text = 'après-midi', votes=20);


choix3.save();

286
Framework Django
Autres requêtes
>>> # Afficher les choix (objets Choice) ayant plus que 4 votes:
>>> Choice.objects.filter(votes__gt=4)
<QuerySet [<Choice: Présentiel>, <Choice: Distantiel>, <Choice: Hybride>,
<Choice: oui, je veux un café>, <Choice: matin>, <Choice: après-midi>]>

#Donner la liste des questions ordonnées par la taille du champ question_tex


>>> Question.objects.order_by(Length('question_text').asc())
<QuerySet [<
Question: Vous voulez prendre un café ou non ?>,
<Question: Vous préférer cours le matin ou l'aprés midi ? >,
<Question: Vous préférer faire la médecine ou l'ingénierie ?>,
<Question: Vous préférez les écoles nationales ou celles de missions ?>,
<Question: vous voulez enseignement à distance ou présentiel?ou enseigne
ment hybride ?>,
<Question: Vous êtes pour l'invertissement dans des projets commerciaux ou des
projets éducatifs ?>]>

287
Framework Django
Autres requêtes
>>> #Donner la liste des questions ordonnées par la taille du champ question_text
from django.db.models.functions import Length
>>> Question.objects.order_by(Length('question_text').desc())
<QuerySet [
<Question: Vous êtes pour l'invertissement dans des projets commerciaux ou des
projets éducatifs ?>,
<Question: vous voulez enseignement à distance ou présentiel?ou enseignement
hybride ?>,
<Question: Vous préférez les écoles nationales ou celles de missions ?>,
<Question: Vous préférer faire la médecine ou l'ingénierie ?>,
<Question: Vous préférer cours le matin ou l'aprés midi ? >,
<Question: Vous voulez prendre un café ou non ?>
]>

288
Framework Django
Autres requêtes
>>> # incrémenter de 10, les votes du choix d’id=1(modifier)
>>> choix_1= Choice.objects.get(id=1)
choix_1.votes+= 10
choix_1.save()

>>> # incrémenter de 10, les votes du choix d’id=1(modifier) par une


expression F(…)
from django.db.models import F
choix_2_question_1= Choice.objects.get(id=2)
choix_2_question_1.votes=F('votes') + 10
choix_2_question_1.save()

Expressions F()
Un objet F() représente la valeur d’un champ de modèle ou d’une colonne
annotée (nommée). Il permet de se référer à des valeurs de champs de modèles
et d’effectuer avec elles des opérations en base de données sans avoir à les
récupérer préalablement de la base de données vers la mémoire Python.
Pour ça, Django utilise un objet F() pour générer une expression SQL qui
décrit l’opération requise au niveau de la base de données. 289
Framework Django
Autres requêtes
>>> from django.db.models import F
>>> from polls.models import Choice, Question
>>> choix_2_question_1= Choice.objects.get(id=2)
>>> print(choix_2_question_1.votes)
30
>>> choix_2_question_1.votes=F('votes') + 15
>>> choix_2_question_1.save()
>>> print(choix_2_question_1.votes)
F(votes) + Value(15)
>>> choix_2_question_1.refresh_from_db()
>>> print(choix_2_question_1.votes)
45 290
Framework Django
Lancement de requêtes SQL brutes

❑ Django propose deux manières d’exécuter des requêtes SQL


brutes : vous pouvez utiliser Manager.raw() pour exécuter des
requêtes brutes et renvoyer des instances de modèles ou vous
pouvez outrepasser complètement la couche des modèles
et exécuter directement du code SQL personnalisé.

❑ Pour SELECT vous pouvez utiliser raw() par contre


pour UPDATE, INSERT ou DELETE il faut utiliser
django.db.connection (voir diapositives 300, 301,…)

291
Framework Django

Lancement de requêtes SQL brutes

• La méthode de gestionnaire raw() peut être utilisée pour


exécuter des requêtes SQL brutes qui renvoient des instances
de modèles :
Manager.raw(raw_query): Cette méthode accepte une requête
SQL brute, l’exécute et renvoie une
instance django.db.models.query.RawQuerySet. Il est possible
alors d’effectuer une boucle sur cette
instance RawQuerySet comme pour un objet QuerySet normal
afin d’accéder aux instances d’objets.

292
Framework Django
Lancement de requêtes SQL brutes: Exemple
py manage.py shell
>>>from polls.models import Choice, Question
>>>L=[p for p in Choice.objects.raw('SELECT * FROM polls_Choice')]
>>>print(L)
[<Choice: Présentiel>, <Choice: Distantiel>, <Choice: Hybride>, <Choice: oui, je
veux un café>, <Choice: non, je ne veux pas un café>, <Choice: matin>,
<Choice: après-midi>]
>>>L[0].votes
20
--------------------------------------------------------------------------------------------
>>> L=[p for p in Choice.objects.raw('SELECT * FROM polls_Question natural join polls_Choice')]
>>> L[0].votes
32
>>> L[0].pub_date
datetime.datetime(2020, 12, 2, 20, 39, 29, 466846)
293
Framework Django
Lancement de requêtes SQL brutes: Exemple
py manage.py shell
>>>from polls.models import Choice, Question
>>>L=[p for p in Choice.objects.raw('SELECT * FROM polls_Choice')]
>>>print(L)
[<Choice: Présentiel>, <Choice: Distantiel>, <Choice: Hybride>, <Choice: oui, je
veux un café>, <Choice: non, je ne veux pas un café>, <Choice: matin>,
<Choice: après-midi>]

>>> detailsChoixselectionnes=[(L[i].choice_text,L[i].votes) for i in range(len(L))]


[('Présentiel', 520), ('Distantiel', 425), ('oui, je veux un café', 6),
('non, je ne veux pas un café', 1), ('matin', 20), ('après-midi', 20)]

294
Framework Django
Lancement de requêtes SQL brutes: Exemple

>>> L=[p for p in Question.objects.raw('SELECT * FROM


polls_Question natural join polls_Choice')]

>>> L[0].votes
32

>>> L[0].pub_date
datetime.datetime(2020, 12, 2, 20, 39, 29, 466846, tzinfo=<UTC>)

295
Framework Django
Lancement de requêtes SQL brutes: Exemple
>>>
from polls.models import Choice, Question
L=[p for p in Question.objects.raw('SELECT * FROM polls_Question natural join
polls_Choice')]
Attributs_selectionnes=list(L[0].__dict__.keys())
DetailsSelection=""
for i in range(len(L)):
Details=[]
for e in Attributs_selectionnes[1:]:
Details+=[(e,getattr(L[i],e))]
DetailsSelection+=str(Details)+'\n'

print(DetailsSelection)

296
Framework Django
[[('id', 2), ('question_text', 'vous voulez enseignement à distance ou
présentiel?ou enseignement hybride ?'), ('pub_date', datetime.datetime(
2021, 12, 11, 10, 3, 18, 989038, tzinfo=<UTC>)), ('choice_text', 'Dista
ntiel'), ('votes', 425), ('question_id', 2)],
[('id', 4), ('question_text', "Vous préférer cours le matin ou l'aprés midi ? "), ('pub_date', d
atetime.datetime(2020, 11, 11, 17, 27, 21, 852167, tzinfo=<UTC>)), ('ch
oice_text', 'oui, je veux un café'), ('votes', 6), ('question_id', 3)],
[('id', 5), ('question_text', "Vous êtes pour l'invertissement dans de
s projets commerciaux ou des projets éducatifs ?"), ('pub_date', dateti
me.datetime(2020, 11, 23, 12, 27, 21, 852167, tzinfo=<UTC>)), ('choice_
text', 'non, je ne veux pas un café'), ('votes', 1), ('question_id', 3)],
[('id', 6), ('question_text', "Vous préférer faire la médecine ou l'
ingénierie ?"), ('pub_date', datetime.datetime(2020, 11, 15, 17, 27, 21
, 852167, tzinfo=<UTC>)), ('choice_text', 'matin'), ('votes', 20), ('qu
estion_id', 4)], [('id', 7), ('question_text', 'Vous préférez les école
s nationales ou celles de missions ?'), ('pub_date', datetime.datetime(
2020, 12, 11, 17, 27, 21, 852167, tzinfo=<UTC>)), ('choice_text', 'aprè
s-midi'), ('votes', 20), ('question_id', 4)]]
297
Framework Django
Lancement de requêtes SQL brutes: Exemple
>>> from polls.models import Choice, Question
>>> L=list(Choice.objects.raw('SELECT * FROM polls_Choice'))
>>> for e in L:
... print("pour le choix numéro:"+str(e.id)+"==> texte:"+e.choice_text+",
les votes="+str(e.votes))
...
pour le choix numéro:1==> texte:Présentiel, les votes=20
pour le choix numéro:2==> texte:Distantiel, les votes=45
pour le choix numéro:4==> texte:Hybride, les votes=15
pour le choix numéro:9==> texte:oui, je veux un café, les votes=6
pour le choix numéro:10==> texte:non, je ne veux pas un café, les votes=1
pour le choix numéro:11==> texte:matin, les votes=20
pour le choix numéro:12==> texte:après-midi, les votes=20
298
Framework Django
from polls.models import Choice, Question
# ci-dessous, problème id non sélectionné
X=[p for p in Choice.objects.raw('SELECT votes FROM polls_Choice')]
print(X)
➔ Voir : Choice.objects.values_list('votes’) diapositive 323

L=[p for p in Choice.objects.raw('SELECT id,votes FROM polls_Choice')]


L=[p.votes for p in L]; print(L)
Choice.objects.raw('update polls_Choice set votes=votes+10')
L=[p for p in Choice.objects.raw('SELECT id,votes FROM polls_Choice')]
L=[p.votes for p in L]; print(L)
l’instruction ci-dessus n’a pas d’effet, raw marche avec les SELECT et
pas update, insert into, delete, ….
299
Framework Django
Lancement de requêtes SQL brutes: Exemple

>>> from polls.models import Choice, Question


>>> Choice.objects.raw('update polls_Choice set votes=votes+10')
l’instruction ci-dessus n’a pas d’effet, raw marche avec les SELECT et pas
update, insert into, delete, ….

Ajout d’annotations

Vous pouvez aussi exécuter des requêtes contenant des


champs qui ne sont pas définis dans le modèle. Par exemple,
il serait possible d’utiliser un alias age pour obtenir une liste
des questions avec leur « âges » calculés comme suit:

300
Framework Django

>>> L=Question.objects.raw("SELECT *, date('Now')-pub_date AS age


FROM polls_Question")
>>> for p in L:
... print("l'age de la question "+str(p.id)+" est:"+str(p.age))
...
l'age de la question 1 est:0
l'age de la question 2 est:0
l'age de la question 3 est:0
l'age de la question 4 est:0
l'age de la question 5 est:0
l'age de la question 6 est:0

301
Framework Django
requete= "SELECT *, julianday(datetime('Now'))-julianday(pub_date) AS age "
requete = requete +" FROM polls_Question"
>>> L=Question.objects.raw(requete)
>>> for p in L:
... print("l'age de la question "+str(p.id)+" est:"+str(p.age))
...
l'age de la question 1 est:16.63683488406241
l'age de la question 2 est:37.936923009343445
l'age de la question 3 est:37.72858967585489
l'age de la question 4 est:25.936923009343445
l'age de la question 5 est:33.72858967585489
l'age de la question 6 est:7.7285896758548915
l'age de la question 8 est:0.12668954860419035
302
Framework Django
Exécution directe de code SQL
• Dans certains cas, même Manager.raw() ne suffit pas : il se peut
que des requêtes doivent être effectuées sans correspondre
proprement à des modèles ou que vous vouliez exécuter
directement des requêtes UPDATE, INSERT ou DELETE.

• L’objet django.db.connection représente la connexion à la base de


données par défaut. Pour utiliser la connexion à la base de
données, appelez connection.cursor() pour obtenir un objet
curseur. Puis, appelez cursor.execute(sql) pour exécuter le code
SQL et cursor.fetchone() ou cursor.fetchall() pour obtenir les lignes
de résultat.

303
Framework Django
Exécution directe de code SQL
>>> from django.db import connection
>>> def mon_code_sql():
... L=[]
... with connection.cursor() as cursor:
... cursor.execute("UPDATE polls_Choice SET votes = votes+1")
... cursor.execute("SELECT * FROM polls_Choice")
... L=cursor.fetchall()
... print(L)

304
Framework Django
Exécution directe de code SQL

>>> mon_code_sql()
[(1, 'Présentiel', 26, 1), (2, 'Distantiel', 51, 1), (4, 'Hybride', 21, 1), (9,
'oui, je veux un café', 12, 2), (10, 'non, je ne veux pas un café', 7, 2),
(11, 'matin', 26, 3), (12, 'après-midi', 26, 3)]

>>> mon_code_sql()
[(1, 'Présentiel', 27, 1), (2, 'Distantiel', 52, 1), (4, 'Hybride', 22, 1), (9,
'oui, je veux un café', 13, 2), (10, 'non, je ne veux pas un café', 8, 2),
(11, 'matin', 27, 3), (12, 'après-midi', 27, 3)]

305
Framework Django
Référence de l’API QuerySet
• Méthodes renvoyant de nouveaux QuerySet: Django fournit un
grand nombre de méthodes d’affinage des QuerySet qui
modifient soit le type de résultats renvoyés par le QuerySet ou sa
façon d’exécuter la requête SQL.

• filter():
filter(**kwargs): Renvoie un nouveau QuerySet contenant les
objets qui répondent aux paramètres de recherche donnés.
• Les paramètres de recherche (**kwargs) doivent être dans le
format décrit dans les diapositives de Recherches dans les
champs ci-dessous. Plusieurs paramètres sont combinés
via AND dans l’instruction SQL sous-jacente.

306
Framework Django
Recherches dans les champs
• Par commodité, lorsqu’aucune recherche particulière n’est
indiquée (comme dans Choice.objects.get(id=4)), Django suppose
que la recherche souhaitée est exact.
• exact: Correspondance exacte. Si la valeur fournie pour la
comparaison vaut None, elle sera interprétée comme un NULL SQL.
Exemples :
Choice.objects.get(id__exact=4)  SELECT ... WHERE id = 4
Choice.objects.get(id__exact=None) SELECT ... WHERE id IS NULL
• iexact: Correspondance exacte insensible à la casse.
Exemple : Choice.objects.get(choice_text__iexact='matin')
 SELECT ... WHERE choice_text like ='matin'

307
Framework Django
Recherches dans les champs
• contains: Test d’inclusion sensible à la casse.
Exemple :
Choice.objects.get(choice_text__contains='matin')
 SELECT ... WHERE choice_text LIKE ='%matin%‘
>>> Choice.objects.get(choice_text__contains='midi') ➔<Choice: après-midi>
>>> Choice.objects.get(choice_text__contains='Midi') ➔ <Choice: après-midi>
(vous constatez que: contains fonctionne comme icontains for SQLite)
• icontains: Test d’inclusion non sensible à la casse.
Exemple :
Choice.objects.get(choice_text__icontains='matin')
 SELECT ... WHERE choice_text ILIKE ='%matin%'
>>> Choice.objects.get(choice_text__icontains='Midi') ➔<Choice: après-midi>
308
Framework Django
Recherches dans les champs
• in: Dans un itérable donné ; souvent une liste, un tuple ou un jeu
ou des chaînes.
Exemple :
Choice.objects.filter(id__in=[1,3,6])
 SELECT ... WHERE id in (1,3,6)
>>> Choice.objects.filter(id__in=[1,4,9]) ➔
<QuerySet [<Choice: Présentiel>, <Choice: Hybride>, <Choice: oui, je veux un café>]>

• Pas de notin

309
Framework Django
Recherches dans les champs

lt : Plus petit à : < gt: Plus grand à : >

lte: Plus petit ou égal à: <= gte: Plus grand ou égal à : >=

startswith:Commence par (sensible à la casse).

istartswith:Commence par (non sensible à la casse).

endswith:Se termine par (sensible à la casse).

iendswith:Se termine par (non sensible à la casse).


• range: Test d’intervalle (inclusif): voir l’Exemple ci-dessous:

310
Framework Django
Recherches dans les champs
>>> import datetime

>>> start_date = datetime.date(2020, 10, 20)

>>> end_date = datetime.date(2020,12,30)

>>> Question.objects.filter(pub_date__range=(start_date, end_date))

<QuerySet [<Question: vous voulez enseignement à distance ou présentiel?ou


enseignement hybride ?>, <Question: Vous voulez prendre un café ou non ?>,

<Question: Vous préférer cours le matin ou l'aprés midi ? >, <Question: Vous êtes
pour l'invertissement dans des projets commerciaux ou des projets éducatifs ?>,
<Question: Vous préférer faire la médecine ou l'ingénierie ?>, <Question: Vous
préférez les écoles nationales ou celles de missions ?>]
311
Framework Django
Recherches dans les champs

Requêtes imbriquées:Exemple :
L= Choice.objects.filter(votes__lt=20)

>>> id_choix_avec_votes_faibles=[p.id for p in L]

>>> idCVF=id_choix_avec_votes_faibles

>>> text_choix_avec_votes_faibles=Choice.objects.filter(id__in=idCVF)
>>> text_choix_avec_votes_faibles

<QuerySet [<Choice: oui, je veux un café>,


>>> <Choice: non, je ne veux pas un café>]>

 SELECT * from Choice WHERE Choice.id IN


(SELECT id FROM Choice WHERE votes<20) 312
Framework Django
Référence de l’API QuerySet
• exclude():
exclude(**kwargs), renvoie un nouveau QuerySet contenant les
objets qui ne répondent pas aux paramètres de recherche donnés.
• Les paramètres de recherche (**kwargs) doivent être dans le
format décrit dans Recherches dans les champs ci-dessus. Plusieurs
paramètres sont combinés via AND dans l’instruction SQL sous-
jacente, et le tout est englobé dans un NOT().

• Cet exemple exclut tous les éléments dont pub_date est


postérieure à 2020,12,30 ET dont la question_text contient le mot
'préférer'

313
Framework Django
Référence de l’API QuerySet
>>>Question.objects.exclude(pub_date__lt=datetime.date(2020,12,3
0),question_text__icontains='préférer')
<QuerySet [<Question: vous voulez enseignement à distance ou présentiel?ou
enseignement hybride ?>, <Question: Vous voulez prendre un café ou non ?>,
<Question: Vous êtes pour l'invertissement dans des projets commerciaux ou
des projets éducatifs ?>, <Question: Vous préférez les écoles nationales ou celles
de missions ?>]>

En termes SQL, cela est  :


SELECT * from Question
WHERE NOT (pub_date<'2020-12-03' AND question_text like 'préférer‘)
cela est  :
SELECT * from Question
WHERE NOT (pub_date <'2020-12-03') OR NOT(question_text like 'préférer')
314
Framework Django
Référence de l’API QuerySet
>>>Question.objects.exclude(question_text__icontains='préférer').excl
ude(question_text__icontains='voulez')
<QuerySet [<Question: Vous êtes pour l'invertissement dans des projets
commerciaux ou des projets éducatifs ?>, <Question: Vous préférez
les écoles nationales ou celles de missions ?>]>

En termes SQL, cela est  :

SELECT * from Choice


WHERE question_text not ILIKE ‘%préférer%'
AND question_text not ilike ‘%voulez%'

315
Framework Django
Référence de l’API QuerySet

• values(*fields, **expressions): renvoie un QuerySet qui renvoie


des dictionnaires lorsqu’on l’utilise de manière itérable, au lieu
d’instances de modèles.

• Chacun de ces dictionnaires représente un objet, les clés


correspondant aux noms d’attributs des objets de modèle.

• L’exemple suivant compare les dictionnaires de values() avec des


objets de modèle normaux :

316
Framework Django
Référence de l’API QuerySet
>>> from polls.models import Choice, Question
>>> Question.objects.filter(question_text__istartswith='vous voulez')
<QuerySet [<Question: vous voulez enseignement à distance ou
présentiel?ou enseignement hybride ?>, <Question: Vous voulez
prendre un café ou non ?>]>
>>> A=Question.objects.filter(question_text__istartswith='vous
voulez').values(); print(A);
<QuerySet [{'id': 1, 'question_text': 'vous voulez enseignement à distance ou
présentiel?ou enseignement hybride ?', 'pub_date': datetime.datetime(2020,
12, 2, 20, 39, 29, 466846, tzinfo=<UTC>)}, {'id': 2, 'question_text': 'Vous voulez
prendre un café ou non ?', 'pub_date': datetime.datetime(2020, 11, 11, 13, 27,
21, 852167, tzinfo=<UTC>)}]>
>>> print(A[0]['pub_date'])
2020-12-02 20:39:29.466846+00:00 317
Framework Django
Les dates
>>> from polls.models import Question

>>> A=Question.objects.dates('pub_date', 'year'); print(A)

<QuerySet [datetime.date(2020, 1, 1)]>

>>> m=Question.objects.dates('pub_date', 'month'); print(m)

<QuerySet [datetime.date(2020, 11, 1), datetime.date(2020, 12, 1)]>

>>> d=Question.objects.dates('pub_date', 'day'); print(d)

<QuerySet [datetime.date(2020, 11, 11), datetime.date(2020, 11, 15),


datetime.date(2020, 11, 23), datetime.date(2020, 12, 2),
datetime.date(2020, 12, 11)]> 318
Framework Django
Les datetimes
>>> from polls.models import Question
>>> h=Question.objects.datetimes('pub_date', 'hour'); print(h)
<QuerySet [datetime.datetime(2020, 11, 11, 13, 0, tzinfo=<UTC>), datetime.datetime(2020,
11, 11, 18, 0, tzinfo=<UTC>), datetime.datetime(2020, 11, 15, 18, 0, tzinfo=<UTC>),
datetime.datetime(2020, 11, 23, 13, 0, tzinfo=<UTC>), datetime.datetime(2020, 12, 2, 20,
0, tzinfo=<UTC>), datetime.datetime(2020, 12, 11, 18, 0, tzinfo=<UTC>)]>
>>> M=Question.objects.datetimes('pub_date', 'minute'); print(M)
<QuerySet [datetime.datetime(2020, 11, 11, 16, 27, tzinfo=<DstTzInfo 'Europe/Istanbul'
+03+3:00:00 STD>), datetime.datetime(2020, 11, 11, 21, 27, tzinfo=<DstTzInfo '
Europe/Istanbul' +03+3:00:00 STD>), datetime.datetime(2020, 11, 15, 21, 27,
tzinfo=<DstTzInfo 'Europe/Istanbul' +03+3:00:00 STD>), datetime.datetime(2020, 11, 23,
16, 27, tzinfo=<DstTzInfo 'Europe/Istanbul' +03+3:00:00 STD>), datetime.datetime(2020,
12, 2, 23, 39, tzinfo=<DstTzInfo 'Europe/Istanbul' +03+3:00:00 STD>), datetime.d
atetime(2020, 12, 11, 21, 27, tzinfo=<DstTzInfo 'Europe/Istanbul' +03+3:00:00 STD>)]>

319
Framework Django
Les datetimes
>>> S=Question.objects.datetimes('pub_date', 'second'); print(S)

<QuerySet [datetime.datetime(2020, 11, 11, 13, 27, 21,


tzinfo=<UTC>), datetime.datetime(2020, 11, 11, 18, 27, 21,
tzinfo=<UTC>), datetime.datetime(2020, 11, 15, 18,27, 21,
tzinfo=<UTC>), datetime.datetime(2020, 11, 23, 13, 27, 21,
tzinfo=<UTC>), datetime.datetime(2020, 12, 2, 20, 39, 29,
tzinfo=<UTC>), datetime.datetime(2020, 12, 11, 18, 27, 21,
tzinfo=<UTC>)]>

320
Framework Django
regex et iregex
• regex: recherche par expression régulière, sensible à la casse.

>>> Question.objects.filter(question_text__regex=r'^Vous êtes +')


<QuerySet [<Question: Vous êtes pour l'invertissement dans des
projets commerciaux ou des projets éducatifs ?>]>

>>> Question.objects.filter(question_text__regex=r'^vous êtes +')


<QuerySet []>

321
Framework Django
regex et iregex
• iregex: recherche par expression régulière, non sensible à la
casse.

>>> Question.objects.filter(question_text__iregex=r'^vous êtes +')


<QuerySet [<Question: Vous êtes pour l'invertissement dans des
projets commerciaux ou des projets éducatifs ?>]>

322
Framework Django
values_list
>>> qs1 = Choice.objects.values_list('votes')
>>> print(qs1)
<QuerySet [(32,), (57,), (27,), (18,), (13,), (32,), (32,)]>
>>> qs2 = Choice.objects.values_list('votes','question_id')
>>> qs2
<QuerySet [(32, 1), (57, 1), (27, 1), (18, 2), (13, 2), (32, 3), (32, 3)]>

intersection(*other_qs): Utilise l’opérateur SQL INTERSECT pour renvoyer les


éléments communs à plusieurs QuerySet (voir l’opérateur & Diapo 325)
difference(*other_qs): Utilise l’opérateur SQL EXCEPT pour ne conserver que
les éléments présents dans un QuerySet mais pas dans d’autres QuerySet.

323
Framework Django
Union, intersection, différence
>>> qs3=Choice.objects.filter(votes__lte=40).values_list('votes'); print(qs3)
<QuerySet [(32,), (27,), (18,), (13,), (32,), (32,)]>
>>> qs4=Choice.objects.filter(votes__gte=2).values_list('votes'); print(qs4)
<QuerySet [(32,), (57,), (27,), (18,), (13,), (32,), (32,)]>
>>> qs5=Choice.objects.filter(votes=57).values_list('votes'); print(qs5)
<QuerySet [(57,)]>
>>> inter=qs3.intersection(qs3,qs4,qs5); print(inter)
<QuerySet []>
>>> unionn= qs3.union(qs3,qs4,qs5); print(unionn)
<QuerySet [(13,), (18,), (27,), (32,), (57,)]>
>>> dif=qs4.difference(qs3,inter); print(dif)
<QuerySet [(57,)]>
324
Framework Django
Opérateurs renvoyant de nouveaux QuerySet
>>> qs3=Choice.objects.filter(votes__lte=40).values_list('votes'); print(qs3)
<QuerySet [(32,), (27,), (18,), (13,), (32,), (32,)]>
>>> qs4=Choice.objects.filter(votes__gte=2).values_list('votes'); print(qs4)
<QuerySet [(32,), (57,), (27,), (18,), (13,), (32,), (32,)]>
>>> inter=qs3&qs4; print(inter)
<QuerySet [(32,), (27,), (18,), (13,), (32,), (32,)]>
>>> unionn= qs3|qs4|qs5; print(unionn)
<QuerySet [(32,), (57,), (27,), (18,), (13,), (32,), (32,)]>

AND (&): Combine deux requêtes QuerySet en utilisant l’opérateur SQL AND (ET).

OR (|):Combine deux requêtes QuerySet en utilisant l’opérateur SQL OR (OU).

325
Framework Django
get()
• get(**kwargs): Renvoie l’objet correspondant aux paramètres de
recherche indiqués dans les diapositives précédentes. Vous devriez
utiliser des critères qui sont garantis comme uniques, tels que des clés
primaires ou des champs disposant d’une contrainte d’unicité (CIN,
CNE, Num_Permis, Num_Passeport,….).

• Si vous pensez qu’une requête ne contient déjà qu’une seule ligne,


vous pouvez utiliser get() sans paramètre pour obtenir l’objet
correspondant à cette ligne : Question.objects.filter(pk=1).get()

• Si get() ne trouve aucun objet, il génère


l’exception Model.DoesNotExist.
• Si get() trouve plus qu’un objet, il génère une
exception Model.MultipleObjectsReturned
326
Framework Django
create()
• create(**kwargs): Une méthode d’agrément pour la création d’un
objet et son enregistrement en une seule méthode.
Ainsi :
Q= Question.objects.create(question_text="Matin ou après-midi ?",
pub_date="2020-12-12")
Est équivalente à:
Q= Question(question_text="Matin ou après-midi ?", pub_date="2020-12-12")
Q.save(force_insert=True)
• Contrainte d’insertion ou de mise à jour: Dans de rares
circonstances, il est nécessaire de pouvoir forcer la méthode save().
Dans ces situations, il est possible de fournir les
paramètres force_insert=True ou force_update=True à la
méthode save().
327
Framework Django

Q=Question(id=8,question_text="Croyez-vous que 2021 sera meilleure que


2020?", pub_date="2021-01-01"); Q.save();

Q=Question(id=8,question_text="Croyez-vous que 2021 sera meilleure


que 2020?", pub_date="2021-01-01") ; Q.save(force_update=True)
328
Framework Django
count()
• count(): Renvoie un nombre entier représentant le nombre
d’objets de base de données correspondant au QuerySet.

• >>> Choice.objects.filter(votes__lte=40)
<QuerySet [<Choice: Présentiel>, <Choice: Hybride>, <Choice: oui, je
veux un café>, <Choice: non, je ne veux pas un café>, <Choice:
matin>, <Choice: après-midi>]>

• >>> N=Choice.objects.filter(votes__lte=40).count(); print(N)


6
• Un appel à count() génère une instruction SELECT COUNT(*)

329
Framework Django
aggregate()
• aggregate(….): Renvoie un dictionnaire de valeurs agrégées
(moyennes, sommes, etc.) calculées dans le QuerySet. Chaque
paramètre d”aggregate() définit une valeur qui sera incluse dans le
dictionnaire renvoyé.
>>> from django.db.models import Count
>>> q=Choice.objects.all().values_list('votes'); print(q)
<QuerySet [(32,), (57,), (27,), (18,), (13,), (32,), (32,)]>
>>> N= Choice.objects.aggregate(Count('votes')); print(N)
{'votes__count': 7}
>>> N= Choice.objects.aggregate(Count('votes',distinct=True)); print(N)
{'votes__count': 5} # notez bien: distinct en miniscule.

330
Framework Django
aggregate()
>>> from django.db.models import Min, Max, Avg, Sum
>>> mi= Choice.objects.aggregate(Min('votes')); print(mi)
{'votes__min': 13}
>>> ma= Choice.objects.aggregate(Max('votes')); print(ma)
{'votes__ max': 57}
>>> S= Choice.objects.aggregate(Sum('votes')); print(S)
{'votes__ sum': 211}
>>> moy= Choice.objects.aggregate(Avg('votes')); print(moy)
{' 'votes__ avg': 30.142857142857142}

331
Framework Django
update()
• update(**kwargs): Effectue une requête de mise à jour SQL pour
les champs indiqués et renvoie le nombre de lignes affectées (qui
n’est pas forcément égal au nombre de lignes mises à jour dans la
mesure où certaines lignes possèdent déjà la nouvelle valeur).
>>> q=Choice.objects.get(choice_text__startswith="oui")
>>> v=q.votes
>>>Choice.objects.filter(choice_text__startswith="oui").update(votes=v+10)
1
>>>Choice.objects.filter(choice_text__startswith="oui").update(votes=votes+10)
Traceback (most recent call last): File "<console>", line 1, in
<module> NameError: name 'votes' is not defined

332
Framework Django
delete()
• delete(): Effectue une requête de suppression SQL de toutes les
lignes de QuerySet et renvoie le nombre d’objets supprimés ainsi
qu’un dictionnaire avec le nombre de suppressions par type
d’objet.
>>> from polls.models import Choice
>>>C=Choice(choice_text="semi présentiel",votes=20, question_id=1)
>>> C.save()
>>> A=Choice.objects.filter(choice_text="semi présentiel"); print(A);
<QuerySet [<Choice: semi présentiel>]>
>>> Choice.objects.filter(choice_text="semi présentiel").delete()
(1, {'polls.Choice': 1})
>>> A=Choice.objects.filter(choice_text="semi présentiel"); print(A);
<QuerySet []> 333
Framework Django
Introduction au site d’administration de Django

Création d’un utilisateur administrateur:


Nous avons d’abord besoin de créer un utilisateur qui peut se connecter au
site d’administration.
Revenez au Terminal (et taper exit() si vous êtes encore sur shell shell
interactif de Python) puis lancez la commande suivante (vous êtes sur venv):

(venv) C:\______________python cigma 20-21\mysite>py manage.py


createsuperuser
Username: saidnouhcigma
Email address: nouh@gmail.com
Password:
Password (again):
Superuser created successfully.

334
Framework Django
Introduction au site d’administration de Django
À présent, ouvrez un navigateur Web et allez à l’URL « /admin/ » de votre
domaine local – par exemple, http://127.0.0.1:8000/admin/. Vous devriez voir
l’écran de connexion à l’interface d’administration , sinon redémarrer le
serveur : (venv) C:\_.......................\mysite>py manage.py runserver

Sinon vérifier : mysite/mysite/urls.py:


from django.contrib import admin
from django.urls import include, path
urlpatterns = [ ………………………..,
path('admin/', admin.site.urls),]

Terminal➔MonDossierDjango\mysite>
py manage.py createsuperuser saidnouh
335 azerty
Framework Django
Introduction au site d’administration de Django

Vous devriez voir quelques types de contenu éditable : groupes et utilisateurs.


Ils sont fournis par django.contrib.auth, le système d’authentification livré avec Django.
Framework Django
Groups➔ + add…
Framework Django
Users➔ + add… =➔ save
Framework Django
Users➔ + add… =➔ save
Framework Django
http://127.0.0.1:8000/admin/auth/user/
Framework Django
http://127.0.0.1:8000/admin/auth/user/2/change/
Framework Django
http://127.0.0.1:8000/admin/auth/user/2/change/
Framework Django
http://127.0.0.1:8000/admin/auth/user/2/change/
Framework Django
Rendre l’application de sondage modifiable via l’interface d’admin

Mais où est notre application de sondage ? Elle n’est pas


affichée sur la page d’index de l’interface d’administration.

Plus qu’une chose à faire : il faut indiquer à l’admin que les


objets Question ont une interface d’administration. Pour
ceci, ouvrez le fichier polls/admin.py et éditez-le de la
manière suivante :

from django.contrib import admin


from .models import Question
admin.site.register(Question)

344
Framework Django
Exploration des fonctionnalités de l’interface d’administration
Maintenant que nous avons inscrit Question dans l’interface
d’administration, Django sait que cela doit apparaître sur la page d’index :

345
Framework Django
Exploration des fonctionnalités de l’interface d’administration

Cliquez sur « Questions ». À présent, vous êtes sur la page « liste pour
modification » des questions. Cette page affiche toutes les questions de la base
de données et vous permet d’en choisir une pour la modifier. Il y a la question
que nous avons créée précédemment :

346
Framework Django
Introduction au site d’administration de Django
Question +add ➔
Framework Django
•Le formulaire est généré automatiquement à partir du modèle Question.
•Les différents types de champs du modèle (DateTimeField, CharField)
correspondent au composant graphique d’entrée HTML approprié. Chaque
type de champ sait comment s’afficher dans l’interface d’administration de
Django.
•La partie inférieure de la page vous propose une série d’opérations :
•Enregistrer – Enregistre les modifications et retourne à la page liste pour
modification de ce type d’objet.
•Enregistrer et continuer les modifications – Enregistre les modifications et
recharge la page d’administration de cet objet.
•Enregistrer et ajouter un nouveau – Enregistre les modifications et charge un
nouveau formulaire vierge pour ce type d’objet.
•Supprimer – Affiche une page de confirmation de la suppression. 348
Framework Django

. Dans notre application de sondage, nous aurons les quatre vues

suivantes :
La page de sommaire des questions – affiche quelques-unes des dernières
questions.
•La page de détail d’une question – affiche le texte d’une question, sans les
résultats mais avec un formulaire pour voter.
•La page des résultats d’une question – affiche les résultats d’une question
particulière.
•Action de vote – gère le vote pour un choix particulier dans une question
précise.

349
Framework Django

from polls.models import Choice, Question


def detail(request, question_id):
Q=Question.objects.get(id=question_id) ; contenu=str(Q)
X=str(question_id)+ ' de contenu = '+contenu
return HttpResponse("You're looking at question "+X)

Taper http://127.0.0.1:8000/polls/1/ dans la barre d’adresses:


➔ Page contenant: You're looking at question 1 de contenu = vous voulez
enseignement à distance ou présentiel?ou enseignement hybride ?

350

Vous aimerez peut-être aussi