Menu

Comment réaliser une “ToDo App” avec Django 3.1 – Partie III

ToDo APP avec Django

Bonjour à tous et bienvenus à la troisième et dernière partie du tutoriel “Comment réaliser une ToDo App avec Django”. Cette fois-ci, nous apprendrons à créer le design d’une Todo App avec Bootstrap et Django. En effet, dans la seconde partie de ce tutoriel, nous avions une application fonctionnelle mais pas très jolie. Dans cet article, nous allons travailler le graphisme de l’application. Et pour cela, on va y ajouter du “Style” pour un effet plus professionnel à l’aide du Framework Bootstrap.

Le Framework Bootstrap

Pour gérer l’aspect de notre application, nous aurons besoin du langage CSS (Cascading Style Sheets). Pour ce faire, nous allons utiliser dans ce tutoriel le célèbre Framework CSS : Bootstrap V5.0.

Bootstrap est un ensemble de fichiers CSS et Javascript. Il nous fournit les outils nécessaires pour construire facilement et rapidement un design propre et élaboré pour notre application.

Pour commencer, nous utiliserons des composants ne nécessitant pas de fonctionner avec Javacript. Pour l’instant, nous n’aurons donc besoin que d’un fichier CSS minifié que nous inclurons dans notre fichier base.html. Il vous faut aller sur le site getbootstrap.com et dans la section jsDelivr copier le lien vers le fichier CSS.

getbootstrap.com - TodoApp avec Bootstrap et Django

Ajoutez le code copié dans la section <head> du fichier base.html se trouvant dans le répertoire templates du projet, ainsi le style sera étendu aux autres fichiers : index.html, update.html et delete.html.

Dans le fichier de base, nous avons aussi modifié l’élément <div> dans lequel nous avons inscrit le bloc content. En effet, nous avons associé à la balise <div> la classe .container qui est un élément de mise en page basique de Bootstrap. Cet élément est un conteneur qui permet de recentrer le contenu de la page web.

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
	    <!-- CSS only -->
	<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">

    <title>
    	{% block title %}       

        {% endblock title %}
    	ToDo App</title>
</head>
<body>

    <div class="container">
        {% block content %}

        {% endblock content %}
    </div>
</body>
</html>

En rafraichissant la page index par exemple, on remarque les changements de typographie et de disposition du contenu.

page index - TodoApp avec Bootstrap et Django

Structuration des templates

Accueil

Pour notre page index (page d’accueil), on peut utiliser un élément conteneur flexible et très pratique pour nous aider à structurer notre liste de tâche : l’élément .card (carte, en français). Les cartes sont généralement composées d’un en-tête .card-header, d’un corps .card-body et d’un pied .card-footer. Dans notre exemple, nous avons mis dans l’en-tête .card-header le titre de l’application et dans le corps .card-body le formulaire de saisie ainsi que la liste des tâches.

Nous plaçons les champs du formulaire <form> dans une <div> à laquelle on associe la classe groupe d’entrée .group-input qui permet de placer le bouton AJOUTER du côté droit de l’entrée {{task_form.title}}. Pour gérer la taille des éléments du formulaire, on utilise les classes de dimensionnement associé à .group-input (exemple : .mb-3 pour définir la taille par défaut d’un groupe d’entrée).

Pour les boutons de formulaires <button>, <input> ou les liens <a>, on utilise la classe bouton .btn, à laquelle on peut associer d’autres classes pour gérer la taille (exemple : .btn-lg) et la couleur (exemple : .btn-primary).

{% extends 'base.html' %}
{% block title %}
Mes tâches |
{% endblock title %}

{% block content %}

<div class="card">
    <div class="card-header">
        <h1 class="text-center">My ToDo App</h1>
    </div>
    <div class="card-body">
        <form method="post">
            {% csrf_token %}
            <div class="input-group mb-3">                
                {{task_form.title}}               
                <button type="submit" class="btn btn-primary btn-lg">AJOUTER</button>
            </div>
        </form>
        <h4 class="bg-secondary text-light py-2 rounded text-center">Ma liste de Tâches </h4>

        <ul class="list-group">
            {% for task in tasks %}
            <li class="list-group-item list-group-item-action">
                {% if task.completed == True %}
                <strike>{{task.title}} </strike>
                {% else %}
                {{task.title}}
                {% endif %}
              <div class="float-end">
                    <a href="{% url 'update' task.id %}" class="btn btn-sm btn-warning">Modifier</a>

                    <a href="{% url 'delete' task.id %}" class="btn btn-sm btn-danger">Supprimer</a>
                </div>
            </li>
            {% endfor %}
        </ul>
    </div>
</div>
{% endblock content %}

En modifiant votre fichier index.html, comme indiqué ci-dessus, vous obtenez la page d’accueil (page principale) suivante :

page index - TodoApp avec Bootstrap et Django

Mise à jour

Pour la page update.html, nous structurons celle-ci à l’aide de la classe .card de la même manière que pour la page d’accueil. Concernant le formulaire, nous utilisons aussi la classe .input-group pour placer la case à cocher du champ completed à gauche du champ de title et le bouton Modifier à sa droite.

{% extends 'base.html' %}
{% block title %}
Modifier une tâche | 
{% endblock title %}
{% block content %}
<div class="card">
    <div class="card-header">
        <h1 class="text-center">ToDo App</h1>
    </div>
    <div class="card-body">
        <form method="post">
            {% csrf_token %}
            <div class="input-group mb-3">
                <div class="input-group-text" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Tâche réalisée">
                    {{edit_task_form.completed}}
                </div>
                {{edit_task_form.title}}
                <button type="submit" class="btn btn-lg btn-info">Modifier</button>
            </div>
        </form>
    </div>
</div>
{% endblock content %}

Infobulle Bootstrap

Nous inscrivons la case à cocher {{edit_task_form.compelted}} dans une <div> utilisée pour afficher une infobulle (tooltip, en anglais) qui, au survol de la case à cocher, nous informe du sens de la case cochée (Tâche réalisée) à l’aide de l’attribut data-bs. Pour faire fonctionner l’infobulle, nous aurons besoin de CSS et de Javascript. Nous allons définir l’infobulle dès à présent. Puis nous ajouterons le code Javascript nécessaire pour la faire fonctionner plus tard (dans la dernière section de ce tutoriel).

Avec data-bs-toggle="tooltip", on indique que l’on veut afficher une infobulle et avec data-bs-placement="bottom", on indique que l’on souhaite que celle-ci s’affiche en bas de l’élément survolé. Le texte à afficher dans l’info-bulle est défini à l’aide de l’attribut title.

Sans le code Javascript, nous obtenons la page ci-après. En survolant la case à cocher, l’information “Tâche réalisée” s’affiche grâce à l’attribut title, qui est un attribut HTML interprétable par les navigateurs web.

page update - TodoApp avec Bootstrap et Django

Suppression

Comme pour les deux autres pages, nous utiliserons la classe .card pour structurer le fichier delete.html. Nous allons afficher le lien d’annulation <a> et l’élément de confirmation <input> sous forme de boutons .btn alignés à l’aide de la classe d-inline-block.

{% extends 'base.html' %}
{% block title %}
Supprimer une tâche | 
{% endblock title %}

{% block content %}
<div class="card">
    <div class="card-header">
        <h1 class="text-center">ToDo App</h1>
    </div>
    <div class="card-body">
    	<h4>Veux-tu supprimer la tâche : <strong>{{task}}</strong>?</h4>
        <form method="post">
            {% csrf_token %}
            <div class="d-inline-block">
    		<a href="{%url 'index'%}" class="btn btn-lg btn-secondary"> Annuler</a>
    		</div>
            <div class="d-inline-block">            	
            		<input type="submit" class="btn btn-lg btn-danger" value="Confirmer">
            </div>
        </form>
    </div>
</div>
{% endblock content %}

Voici à quoi devra ressembler la page de suppression.

page delete - TodoApp avec Bootstrap et Django

Améliorer les formulaires

Nous venons de voir avec quelle facilité il est possible de gérer le graphisme grâce à Bootstrap. Cependant, vous avez probablement remarqué que les champs de saisie de texte ne sont pas terribles. Nous allons y remédier en apportant quelques changements au fichier forms.py.

Dans Django, les champs de formulaires sont associés à des composants de formulaires, appelés plus communément Widgets. Ces derniers gèrent la production de code HTML des éléments de saisie, ainsi que l’extraction des données envoyées.

Par exemple un champ de formulaire forms.CharField() est associé à un widget Django par défaut forms.widgets.TextInput() qui génère une balise HTML d’entrée de texte <input type="text" ...>.

Une classe widget fournit l’attribut attrs qui correspond à un dictionnaire contenant les attributs HTML à définir pour le composant de formulaire à afficher.

A travers ce dictionnaire, on a utilisé la classe .form-control pour mettre en forme le champs title ainsi que la classe .form-control-lg pour fixer sa taille, grande en l’occurrence. On a aussi défini l’attribut HTML placeholder pour renseigner le texte indicatif "Nouvelle tâche …" dans le champs du formulaire, qui s’efface au moment de la saisie d’un texte.

from django import forms
from django.forms import ModelForm
from .models import Task

class TaskForm(forms.ModelForm):
    class Meta:
        model = Task
        fields = "__all__"
        #fields = ('title',)

    title = forms.CharField(
    	widget=forms.TextInput(
    		attrs={
    		"class": "form-control form-control-lg",
    		"placeholder": "Nouvelle tâche ...",
    		}),)

Après avoir procédé aux modifications du fichier forms.py, vous pouvez rafraîchir la page d’accueil pour voir la mise en forme du formulaire de saisie d’une nouvelle tâche.

Formulaire de la page index - TodoApp avec Bootstrap et Django

Cliquer sur le bouton Modifier d’une tâche de la liste pour être redirigé vers la page de mise à jour. Vous constaterez là aussi la nouvelle mise en forme du formulaire.

Formulaire de la page update - TodoApp avec Bootstrap et Django

Les fichiers statiques

Généralement les sites web servent des fichiers comme des images, des fichiers CSS ou encore Javascript. On appelle ces derniers fichiers statiques. Dans Django, on gère ces fichiers avec django.contrib.staticfiles.

Dans cette section, nous allons voir comment personnaliser le style à notre goût en ajoutant du code CSS. Nous verrons ensuite comment ajouter du code Javascript pour faire fonctionner l’infobulle, que nous avions définie plus haut.

Pour ce faire, nous allons créer à la racine du répertoire ToDoAppProject le répertoire static. On y crée le dossier css qui contiendra le fichier style.css et le dossier js dans lequel on créera le fichier script.js.

Arborescence du projet - TodoApp avec Bootstrap et Django

Personnaliser le style

Pour ce tutoriel, nous allons apporter de légères modifications au style. Ajoutez le code ci-après dans le fichier style.css pour changer la couleur d’arrière plan du corps des pages et gérer la position et la taille du conteneur principal.

body{
    background-color: #547086;
}
.container{
    margin-top: 100px;
    max-width: 700px;
}

Infobulle Bootstrap

Pour faire fonctionner l’infobulle de la case à cocher du formulaire de mise à jour, nous ajouterons du code Javascript. Pour cela, il faut d’abord vous reconnecter à la page getbootstrap.com. Une fois sur le site, copiez le lien vers les plugins Javascript et Popper (utilisé par les infobulles).

Plugins Javascript et Popper - TodoApp avec Bootstrap et Django

On ajoute le code copié à la fin de la page base.html juste avant la balise </body>.

...
<body>
    <div class="container">
        {% block content %}

        {% endblock content %}
    </div>
       <!-- JavaScript Bundle with Popper -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-b5kHyXgcpbZJO/tY9Ul7kGkf1S0CWuKcCD38l8YkeH8z8QjE0GmW1gYU5S9FOnJ0" crossorigin="anonymous"></script>

</body>
</html>

Dans le fichier update.html, on ajoute un identifiant id="competed" à la <div> utilisée pour afficher l’infobulle.

...
        <form method="post">
            {% csrf_token %}
            <div class="input-group mb-3">
                <div id="completed" class="input-group-text" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Tâche réalisée">
                    {{edit_task_form.completed}}
                </div>
                {{edit_task_form.title}}
                <button type="submit" class="btn btn-lg btn-info">Modifier</button>
            </div>
        </form>
...

Dans le fichier script.js, on ajoute le script suivant pour activer l’infobulle.

var exampleEl = document.getElementById('completed')
        var tooltip = new bootstrap.Tooltip(exampleEl, {boundary: 'window'})

Chargement des fichiers statiques

On doit définir le chemin vers le dossier static en ajoutant la liste STATICFILES_DIRS du fichier de configuration settings.py.

STATICFILES_DIRS = [
    BASE_DIR / "static",
]

Pour servir les fichiers statiques dans les templates, on doit les charger avec la balise {% load static %} qu’il faut placer en haut du ficher base.html. On ajoute ensuite le lien vers le fichier CSS dans la section <head> au-dessus de <title> à l’aide de la balise {% static %} qui génère le chemin vers le fichier style.css. De façon similaire, on ajoute la balise <script> à la fin du fichier et on génère le chemin vers le fichier script.js.

{% load static %}
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
	    <!-- CSS only -->
	<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">
	<link rel="stylesheet" href="{% static 'css/style.css'%}">
    <title>
    	{% block title %}
        {% endblock title %}
    	ToDo App
    </title>
</head>
<body>
    <div class="container">
        {% block content %}
        {% endblock content %}
    </div>
       <!-- JavaScript Bundle with Popper -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-b5kHyXgcpbZJO/tY9Ul7kGkf1S0CWuKcCD38l8YkeH8z8QjE0GmW1gYU5S9FOnJ0" crossorigin="anonymous"></script>
        <!-- tooltip -->
    <script src="{% static 'js/script.js'%}"></script>	
</body>
</html>

Vous pouez recharger la page d’accueil une dernière fois et vérifier que les changement CSS on été appliqués correctement.

Page d'accueil finale - TodoApp avec Bootstrap et Django

Sur la page de mise à jour, vous pouvez vérifier que l’infobulle apparaît au survol de la case à cocher.

Page de mise à jour finale - TodoApp avec Bootstrap et Django

N’oubliez pas de vérifier que la page de suppression est fonctionnelle.

Page de suppression finale - TodoApp avec Bootstrap et Django

Conclusion

Félicitations ! En suivant ce tutoriel, découpé en trois parties, vous avez réussi à créer une application ToDoApp totalement fonctionnelle. Vous avez créé votre projet Django, appris à définir les modèles de données et interagi avec la base de données. Vous avez aussi réussi à créer les vues, les templates et les URLs de l’application. Enfin, vous avez utilisé Bootstrap pour gérer le graphisme et appris à servir les fichiers statiques de l’application.