Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente |
public:appro-s7:td_web_hamsters [2023/11/04 16:43] – edauce | public:appro-s7:td_web_hamsters [2024/11/02 17:03] (Version actuelle) – [Indications] edauce |
---|
Vous pouvez bien sûr conserver la structure de base qui est d'avoir un ensemble de créatures et/ou de personnages, et un ensemble de lieux avec des fonctions différentes (par exemple, pour un centre d'entrainement : terrain de foot, cantine, salle de muscu, dortoir). Il peut y avoir des variantes : Pour une maison des poupées il peut y avoir plusieurs chambres, une pièce commune, un jardin et une terrasse. Laissez libre cours à votre imagination. | Vous pouvez bien sûr conserver la structure de base qui est d'avoir un ensemble de créatures et/ou de personnages, et un ensemble de lieux avec des fonctions différentes (par exemple, pour un centre d'entrainement : terrain de foot, cantine, salle de muscu, dortoir). Il peut y avoir des variantes : Pour une maison des poupées il peut y avoir plusieurs chambres, une pièce commune, un jardin et une terrasse. Laissez libre cours à votre imagination. |
| |
Les créatures passent par différents états au cours de la journée en fonction des lieux qu'ils visitent. | Les personnages passent par différents états au cours de la journée en fonction des lieux qu'ils visitent. |
| |
<note tip> ** Rappel ** | <note tip> ** Rappel ** |
| |
Reprenez cette structure de données de type ''Animal'' -- ''Equipement'' utilisée dans les TD précédents | Reprenez cette structure de données de type ''Personnage'' -- ''Equipement'' utilisée dans les TD précédents |
{{ :public:appro-s7:mie-_ea.png?600| }} | {{ :public:appro-s7:mie-_ea_-_projet_1_.png?600 |}} |
</note> | </note> |
| |
| |
class Character(models.Model): | class Character(models.Model): |
id_char = models.CharField(max_length=100, primary_key=True) | id_character = models.CharField(max_length=100, primary_key=True) |
etat = models.CharField(max_length=20) | etat = models.CharField(max_length=20) |
type = models.CharField(max_length=20) | type = models.CharField(max_length=20) |
race = models.CharField(max_length=20) | team = models.CharField(max_length=20) |
photo = models.CharField(max_length=200) | photo = models.CharField(max_length=200) |
lieu = models.ForeignKey(Equipement, on_delete=models.CASCADE) | lieu = models.ForeignKey(Equipement, on_delete=models.CASCADE) |
def __str__(self): | def __str__(self): |
return self.id_char | return self.id_character |
</code> | </code> |
| |
| |
Vous deviez obtenir une interface simple de ce type: | Vous deviez obtenir une interface simple de ce type: |
| |
{{ :public:appro-s7:simple-hamster.png?600 |}} | {{ :public:appro-s7:simple-hamster.png?600 |}} |
| |
Vous pouvez exploiter les photos qui sont définies dans les attributs du modèle pour obtenir par exemple: | Vous pouvez exploiter les photos qui sont définies dans les attributs du modèle pour obtenir par exemple: |
| |
{{ :public:appro-s7:screenshot.png?500 |}} | {{ :public:appro-s7:animalerie_web.png?800 |}} |
| |
==== Formulaires Django ==== | ==== Formulaires Django ==== |
(repris du tutoriel {{https://tutorial.djangogirls.org/fr/django_forms/}}) | (repris du tutoriel {{https://tutorial.djangogirls.org/fr/django_forms/}}) |
| |
La dernière chose que nous voulons faire sur notre site web, c'est créer une manière de mettre a jour l'etat des animaux. Les formulaires (forms) vont nous permettre de rendre le site dynamique ! | La dernière chose que nous voulons faire sur notre site web, c'est créer une manière de mettre a jour l'etat des personnages. Les formulaires (forms) vont nous permettre de rendre le site dynamique ! |
| |
Comme toutes les choses importantes dans Django, les formulaires ont leur propre fichier : ''forms.py''. | Comme toutes les choses importantes dans Django, les formulaires ont leur propre fichier : ''forms.py''. |
fields = ('lieu',) | fields = ('lieu',) |
</code> | </code> |
<html> | Tout d'abord, nous avons besoin d'importer les formulaires Django (''from django import forms''), puis notre modèle Character (''from .models import Character''). |
<p>Tout d'abord, nous avons besoin d'importer les formulaires Django (<code>from django import forms</code>), puis notre modèle <code>Character</code> (<code>from .models import Character</code>).</p> | |
<p>Comme vous l'avez probablement deviné, <code>MoveForm</code> est le nom de notre formulaire. Nous avons besoin d'indiquer à Django que ce formulaire est un <code>ModelForm</code> (pour que Django fasse certaines choses automatiquement pour nous). Pour cela, nous utilisons <code>forms.ModelForm</code>.</p> | Comme vous l'avez probablement deviné, ''MoveForm'' est le nom de notre formulaire. Nous avons besoin d'indiquer à Django que ce formulaire est un ModelForm (pour que Django fasse certaines choses automatiquement pour nous). Pour cela, nous utilisons ''forms.ModelForm''. |
<p>Ensuite, nous avons la <code>class Meta</code> qui nous permet de dire à Django quel modèle il doit utiliser pour créer ce formulaire (<code>model = Character</code>).</p> | |
<p>Enfin, nous précisions quel⋅s sont le⋅s champ⋅s qui doivent figurer dans notre formulaire. Dans notre cas, nous souhaitons que seul le <code>lieu</code> apparaisse dans notre formulaire. </p> | Ensuite, nous avons la class Meta qui nous permet de dire à Django quel modèle il doit utiliser pour créer ce formulaire (''model = Character''). |
<p>Et voilà, c'est tout ! Tout ce qu'il nous reste à faire, c'est d'utiliser ce formulaire dans une <em>vue</em> et de l'afficher dans un template.</p> | |
<p>Nous allons donc une nouvelle fois suivre le processus suivant et créer : un lien vers la page, une URL, une vue et un template.</p> | Enfin, nous précisions quel⋅s sont le⋅s champ⋅s qui doivent figurer dans notre formulaire. Dans notre cas, nous souhaitons que seul le lieu apparaisse dans notre formulaire. |
</html> | |
| Et voilà, c'est tout ! Tout ce qu'il nous reste à faire, c'est d'utiliser ce formulaire dans une vue et de l'afficher dans un template. |
| |
| Nous allons donc une nouvelle fois suivre le processus suivant et créer : un lien vers la page, une URL, une vue et un template. |
| |
==== Lien vers une page contenant le formulaire ==== | ==== Lien vers une page contenant le formulaire ==== |
| |
Il est temps d'ouvrir playground/templates/playground/base.html dans l'éditeur de code et ajouter un lien vers la vue ''character_detail''. | Il est temps d'ouvrir ''playground/templates/playground/base.html'' dans l'éditeur de code et ajouter un lien vers la vue ''character_detail''. |
<code html> | <code html> |
<a href="{% url 'character_detail' id_character=character.id_character %}">{{ character.id_character }}</a> | <a href="{% url 'character_detail' id_character=character.id_character %}">{{ character.id_character }}</a> |
==== La vue character_detail ==== | ==== La vue character_detail ==== |
| |
<html> | |
<p>Ouvrez maintenant le fichier <code>playground/views.py</code> dans l'éditeur de code et ajoutez les lignes suivantes avec celles du <code>from</code> qui existent déjà :</p> | Ouvrez maintenant le fichier ''playground/views.py'' dans l'éditeur de code et ajoutez les lignes suivantes avec celles du ''from'' qui existent déjà : |
</html> | |
<code python> | <code python> |
from django.shortcuts import render, get_object_or_404, redirect | from django.shortcuts import render, get_object_or_404, redirect |
{'character': character, 'lieu': lieu, 'form': form}) | {'character': character, 'lieu': lieu, 'form': form}) |
</code> | </code> |
<html> | |
| |
<p>Afin de pouvoir créer un nouveau formulaire <code>Move</code>, nous avons besoin d'appeler la fonction <code>MoveForm()</code> et de la passer au template. Nous reviendrons modifier cette <em>vue</em> plus tard, mais pour l'instant, créons rapidement un template pour ce formulaire.</p> | Afin de pouvoir créer un nouveau formulaire ''Move'', nous avons besoin d'appeler la fonction ''MoveForm()'' et de la passer au template. Nous reviendrons modifier cette //vue// plus tard, mais pour l'instant, créons rapidement un template pour ce formulaire. |
| |
</html> | |
| |
| |
==== Template ==== | ==== Template ==== |
| |
<html> | Nous avons à présent besoin de créer un fichier ''character_detail.html'' dans le dossier ''playground/templates/playground'' et de l'ouvrir dans l'éditeur de code. Afin que notre formulaire fonctionne, nous avons besoin de plusieurs choses : |
<p>Nous avons à présent besoin de créer un fichier <code>character_detail.html</code> dans le dossier <code>playground/templates/playground</code> et de l'ouvrir dans l'éditeur de code. Afin que notre formulaire fonctionne, nous avons besoin de plusieurs choses :</p> | |
<ul> | - Nous avons besoin d'afficher le formulaire. Pour cela, nous n'avons qu'à utiliser ''{{ form.as_uk }}''. |
<li>Nous avons besoin d'afficher le formulaire. Pour cela, nous n'avons qu'à utiliser <code>{{ form.as_uk }}</code>.</li> | - La ligne précédente va avoir besoin d'être entourée des balises HTML ''<form method="POST">...</form>''. |
<li>La ligne précédente va avoir besoin d'être entourée des balises HTML <code><form method="POST">...</form></code>.</li> | - Nous avons besoin d'un bouton ''Valider''. Nous allons le créer à l'aide d'un bouton HTML : ''<button type="submit">Valider</button>''. |
<li>Nous avons besoin d'un bouton <code>Valider</code>. Nous allons le créer à l'aide d'un bouton HTML : <code><button type="submit">Valider</button></code>.</li> | - Enfin, nous devons ajouter ''{% csrf_token %}'' juste après ''<form ...>''. C'est très important car c'est ce qui va permettre de sécuriser votre formulaire ! Si vous oubliez ce détail, Django se plaindra lorsque vous essaierez de sauvegarder le formulaire: |
<li>Enfin, nous devons ajouter <code>{% csrf_token %}</code> juste après <code><form ...></code>. C'est très important car c'est ce qui va permettre de sécuriser votre formulaire ! Si vous oubliez ce détail, Django se plaindra lorsque vous essaierez de sauvegarder le formulaire:</li> | |
</ul> | |
<p><img src="images/csrf2.png" alt="CSFR Forbidden page"></p> | Ok, voyons maintenant à quoi devrait ressembler le HTML contenu dans le fichier ''character_detail.html'' : |
<p>Ok, voyons maintenant à quoi devrait ressembler le HTML contenu dans le fichier <code>character_detail.html</code> : | |
</html> | |
<code html> | <code html> |
{% extends 'playground/base.html' %} | {% extends 'playground/base.html' %} |
{% endblock %} | {% endblock %} |
</code> | </code> |
<html> | |
<p>Rafraîchissons la page ! Et voilà : le formulaire s'affiche sous la forme d'une liste d'options!</p> | Rafraîchissons la page ! Et voilà : le formulaire s'affiche sous la forme d'une liste d'options! |
<p>Mais attendez une minute! Lorsque vous sélectionnez une option, que se passera-t-il?</p> | |
| Mais attendez une minute! Lorsque vous sélectionnez une option, que se passera-t-il? |
Rien! | Rien! |
Retournons à notre <em>vue</em>.</p> | Retournons à notre //vue//. |
</html> | |
| |
==== Sauvegarder le contenu du formulaire ==== | ==== Sauvegarder le contenu du formulaire ==== |
<html> | |
<p>Ouvrez à nouveau <code>blog/views.py</code> dans l'éditeur de code. Actuellement, <code>post_new</code> n'est composé que des lignes de code suivantes :</p> | Ouvrez à nouveau ''blog/views.py'' dans l'éditeur de code. Actuellement, ''post_new'' n'est composé que des lignes de code suivantes : |
</html> | |
<code python> | <code python> |
def character_detail(request, id_character): | def character_detail(request, id_character): |
</code> | </code> |
| |
<html> | |
<p>Lorsque nous envoyons notre formulaire, nous revenons à la même vue. Cependant, nous récupérons les données dans <code>request</code>, et plus particulièrement dans <code>request.POST</code>. Vous rappelez-vous comment dans le fichier HTML, notre définition de la variable <code><form></code> avait la méthode <code>method="POST"</code>? Tous les champs du formulaire se trouvent maintenant dans <code>request.POST</code>. Veillez à ne pas renommer <code>POST</code> en quoi que ce soit d'autre : la seule autre valeur autorisée pour <code>method</code> est <code>GET</code>. Malheureusement, nous n'avons pas le temps de rentrer dans les détails aujourd'hui.</p> | Lorsque nous envoyons notre formulaire, nous revenons à la même vue. Cependant, nous récupérons les données dans ''request'', et plus particulièrement dans ''request.POST''. Vous rappelez-vous comment dans le fichier HTML, notre définition de la variable ''form'' avait la méthode ''method=POST''? Tous les champs du formulaire se trouvent maintenant dans ''request.POST''. Veillez à ne pas renommer ''POST'' en quoi que ce soit d'autre : la seule autre valeur autorisée pour ''method'' est ''GET''. Malheureusement, nous n'avons pas le temps de rentrer dans les détails aujourd'hui. |
<p>Donc dans notre <em>vue</em> nous avons deux situations différentes à gérer : la première quand on accède à la page pour la première fois et nous voulons un formulaire vide, et la seconde quand on revient à la <em>vue</em> avec les données que l'on a saisies dans le formulaire. Pour gérer ces deux cas, nous allons utiliser une condition | |
</html> | Donc dans notre //vue// nous avons deux situations différentes à gérer : la première quand on accède à la page pour la première fois et nous voulons un formulaire vide, et la seconde quand on revient à la //vue// avec les données que l'on a saisies dans le formulaire. Pour gérer ces deux cas, nous allons utiliser une condition |
| |
<code python> | <code python> |
</code> | </code> |
| |
<html> | |
<p>Il faut maintenant remplir à l'endroit des pointillés <code>[...]</code>. Si <code>method</code> contient <code>POST</code> alors on veut construire le <code>MoveForm</code> avec les données du formulaire, n'est-ce pas ? Nous allons le faire comme cela : | Il faut maintenant remplir à l'endroit des pointillés ''[...]''. Si ''method'' contient ''POST'' alors on veut construire le ''MoveForm'' avec les données du formulaire, n'est-ce pas ? Nous allons le faire comme cela : |
</html> | </html> |
<code python> | <code python> |
form = MoveForm(request.POST, instance=character) | form = MoveForm(request.POST, instance=character) |
</code> | </code> |
<html> | |
<p>La prochaine étape est de vérifier que le formulaire a été rempli correctement (tous les champs obligatoires ont été remplis et aucune valeur incorrecte n'a été envoyée). Nous allons faire ça en utilisant <code>form.is_valid()</code>.</p> | La prochaine étape est de vérifier que le formulaire a été rempli correctement (tous les champs obligatoires ont été remplis et aucune valeur incorrecte n'a été envoyée). Nous allons faire ça en utilisant ''form.is_valid()''. |
<p>Testons donc si notre formulaire est valide et, si c'est le cas, sauvegardons-le !</p> | |
</html> | Testons donc si notre formulaire est valide et, si c'est le cas, sauvegardons-le ! |
<code python> | <code python> |
if form.is_valid(): | if form.is_valid(): |
</code> | </code> |
| |
<html> | |
<p>En gros, nous effectuons deux choses ici : nous sauvegardons le nouvel état du personnage grâce à <code>form.save</code> et nous mettons à jour l'occupation des lieux. Rappelez vous, tout déplacement du personnage s'acccompagne d'un changement d'occupation. Nous devons également modifier les lieux. <code>ancien_lieu.save()</code> et <code>nouveau_lieu.save()</code> sauvegarderont les changements. Et voilà, la mise à jour est enregistrée !</p> | |
<p>Enfin, ce serait génial si nous pouvions tout de suite aller à la page <code>character_detail</code> avec le contenu que nous venons de créer. Pour cela, nous avons besoin d'importer une dernière chose : | |
| |
<p>Maintenant, nous allons ajouter la ligne qui signifie "aller à la page <code>character_detail</code> pour le changement qui vient d'être enregistré</p> | En gros, nous effectuons deux choses ici : nous sauvegardons le nouvel état du personnage grâce à ''form.save'' et nous mettons à jour l'occupation des lieux. Rappelez vous, tout déplacement du personnage s'acccompagne d'un changement d'occupation. Nous devons également modifier les lieux. ''ancien_lieu.save()'' et ''nouveau_lieu.save()'' sauvegarderont les changements. Et voilà, la mise à jour est enregistrée ! |
</html> | |
| Enfin, ce serait génial si nous pouvions tout de suite aller à la page ''character_detail'' avec le contenu que nous venons de créer. Pour cela, nous avons besoin d'importer une dernière chose : |
| |
| Maintenant, nous allons ajouter la ligne qui signifie "aller à la page ''character_detail'' pour le changement qui vient d'être enregistré. |
<code python> | <code python> |
return redirect('character_detail', id_character=id_character) | return redirect('character_detail', id_character=id_character) |
</code> | </code> |
<html> | |
<p><code>character_detail</code> est le nom de la vue où nous voulons aller. | |
| |
<p>Voyons à quoi ressemble maintenant notre <em>vue</em> ?</p> | ''character_detail'' est le nom de la vue où nous voulons aller. |
</html> | |
| Voyons à quoi ressemble maintenant notre //vue// ? |
| |
<code python> | <code python> |
{'character': character, 'lieu': lieu, 'form': form}) | {'character': character, 'lieu': lieu, 'form': form}) |
</code> | </code> |
<html> | |
<p>Voyons si ça marche. Allez à l'adresse <a href="http://127.0.0.1:8000/character/Tic/" target="_blank">http://127.0.0.1:8000/character/Tic/</a>, selectionnez un nouveau lieu, sauvegardez ... et voilà ! La mise a jour est prise en compte !</p> | Voyons si ça marche. Allez à l'adresse ''http://127.0.0.1:8000/character/Tic/'', selectionnez un nouveau lieu, sauvegardez ... et voilà ! La mise a jour est prise en compte ! |
</html> | |
| |
==== Modèle complet ==== | ==== Modèle complet ==== |
</code> | </code> |
| |
==== Encore un petit effort : déployons ! ==== | |
<html> | |
<p>Nos modifications fonctionnent-elles sur PythonAnywhere ? Pour le savoir, déployons à nouveau !</p> | |
<ul> | |
<li>Tout d'abord, commitez votre nouveau code et pushez le à nouveau sur GitHub:</li> | |
</ul> | |
<p></p><p class="code-label">command-line</p><p></p> | |
<pre><code>$ git status | |
$ git add . | |
$ git status | |
$ git commit -m "Added views to create/edit blog post inside the site." | |
$ git pus | |
</code></pre><ul> | |
<li>Puis, dans la console bash de <a href="https://www.pythonanywhere.com/consoles/" target="_blank">PythonAnywhere</a>:</li> | |
</ul> | |
<p></p><p class="code-label">Ligne de commande PythonAnywhere</p><p></p> | |
<pre><code>$ cd ~/<your-pythonanywhere-domain>.pythonanywhere.com | |
$ git pull | |
[...] | |
</code></pre><p>(N’oubliez pas de remplacer <code><your-pythonanywhere-domain></code> avec votre propre sous-domaine PythonAnywhere, sans les chevrons.)</p> | |
<ul> | |
<li>Enfin, allez sur <a href="https://www.pythonanywhere.com/web_app_setup/" target="_blank">"Web" page</a> (utilisez le bouton de menu en haut à droite de la console) et cliquez <strong>Reload</strong>. Actualisez votre blog <a href="https://subdomain.pythonanywhere.com" target="_blank">https://subdomain.pythonanywhere.com</a> pour voir les changements.</li> | |
</ul> | |
<p>Et normalement c'est tout ! Félicitations ! :)</p> | |
| |
| |
</section> | Et normalement c'est tout ! Félicitations ! :) |
</html> | |