Menu

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

ToDo APP avec Django

Bonjour à tous, dans cet article, nous allons voir comment réaliser une application avec Django de A à Z. Pour ce tutoriel, mon choix s’est porté sur une application très simple pour débutants: la fameuse “TODO APP”. Ce tutoriel sera découpé en trois parties.

Dans cette première partie, on va d’abord préparer notre environnement de développement, chose qu’on a vu auparavant dans cet article. Et on verra ensuite comment créer nos modèles de données et gérer ces données avec les outils fournis par Django. Les modèles nous permettront de stocker et gérer les tâches que nous voudrons enter dans notre ToDo App. Si vous souhaitez suivre le tutoriel avec le projet entier en main, sachez que le code source est disponible sur GitHub.

Préparation et configuration de base

Nous avons déjà vu cette section en détail dans ce précédent article. Je pense donc que vous n’avez pas besoin de trop d’explications pour exécuter et comprendre les étapes énoncées ci-après.

Créer le répertoire du projet « todoapp »

Dans un répertoire que j’ai créé pour y mettre tous mes projets de développement et que j’ai nommé DEV, je crée le répertoire ToDoAppProject qui contiendra mon application.

D:\>mkdir DEV
D:\>cd DEV
D:\DEV>mkdir ToDoAppProject

Créer et activer l’environnement virtuel

Nous avons déjà parlé, dans cet article, de l’utilité d’avoir un environnement virtuel pour isoler notre projet. On se positionne à la racine de notre répertoire ToDoAppProject, on crée notre environnement virtuel venv et on l’active.

D:\DEV>cd ToDoAppProject
D:\DEV\ToDoAppProject>python -m venv venv
D:\DEV\ToDoAppProject>venv\Scripts\activate

Installer Django

Une fois l’environnement virtuel activé, on installe Django.

(venv) D:\DEV\ToDoAppProject>python -m pip install --upgrade pip
(venv) D:\DEV\ToDoAppProject>pip install django
Collecting django
  Downloading Django-3.1.7-py3-none-any.whl (7.8 MB)
     |████████████████████████████████| 7.8 MB 409 kB/s
Collecting pytz
  Downloading pytz-2021.1-py2.py3-none-any.whl (510 kB)
     |████████████████████████████████| 510 kB 504 kB/s
Collecting sqlparse>=0.2.2
  Using cached sqlparse-0.4.1-py3-none-any.whl (42 kB)
Collecting asgiref<4,>=3.2.10
  Using cached asgiref-3.3.1-py3-none-any.whl (19 kB)
Installing collected packages: sqlparse, pytz, asgiref, django
Successfully installed asgiref-3.3.1 django-3.1.7 pytz-2021.1 sqlparse-0.4.1

Créer le projet « todoapp »

(venv) D:\DEV\ToDoAppProject>django-admin startproject todoapp

Créer l’application « todolist »

On se positionne à la racine du répertoire du projet todoapp et on crée notre application todolist.

(venv) D:\DEV\ToDoAppProject>cd todoapp
(venv) D:\DEV\ToDoAppProject\todoapp>python manage.py startapp todolist

Ajouter l’application

On ajoute l’application todolist à la liste INSTALLED_APP du fichier settings.py du projet.

INSTALLED_APPS = [
            'django.contrib.admin',
            'django.contrib.auth',
            'django.contrib.contenttypes',
            'django.contrib.sessions',
            'django.contrib.messages',
            'django.contrib.staticfiles',
            'todolist',
]

Modèles de données et administration

Notre ToDo App consistera, dans un premier temps, à lister les tâches que nous voudrons effectuer. Il nous faudra alors les stocker dans une base de données et pour ce faire, nous utilisons les modèles.

Les modèles de données

Le modèle

Dans cet article, nous avons déjà vu à quoi servent les modèles de l’architecture MVT sur laquelle se base le Framework Django. Pour rappel, les modèles définissent la structure des données sous forme de classes d’objets. Ces classes interagissent avec la base de données pour permettre le stockage et la gestion de ces données. Un modèle correspond généralement à une table de base de données.

Mapping d’objets relationnels

L’interaction entre modèle et base de données s’effectue via un ORM (Object Relational Mapping, en anglais). L’ORM joue en quelque sorte le rôle de traducteur. Il traduit les requêtes SQL, qui permettent de consulter et d’interagir avec une base de données relationnelle, en langage Python, ainsi que les résultats des requêtes SQL en objets Python.

Créer un modèle

Un modèle est représenté par une classe qui hérite de django.db.models.Model. Les attributs d’une classe correspondent aux champs d’une table de base de données. Par défaut, SQLite est la base de données utilisée par Django pour stocker les données.

Pour ce tutoriel, nous allons créer un seul modèle, que l’on appellera Task (tâche, en français). Le modèle aura trois attributs :

title : c’est le titre de la tâche, défini en tant que chaîne de caractère Charfield avec une longueur maximale de 250 caractères.

completed : c’est le champ qui indique si la tâche a été réalisée ou non. c’est un booléen BooleanField dont la valeur par défaut est False (tâche non réalisée).

created : ce champ indique le moment de création de la tâche. En assignant auto_now_add à True, la date de création de la tâche sera ajoutée automatiquement au moment de la création de la tâche.

INFO En plus des trois attributs créés, Django ajoute automatiquement un champs id correspondant à la clé primaire permettant d’identifier un objet (dans notre cas une tâche) de manière unique : id=models.AutoField(primary_key=True).

On ajoute en dernier la méthode __str()__, une méthode dite “magique” selon la documentation officielle de Django, qui permet de représenter ou d’afficher un objet (instance de modèle) sous forme de chaîne de caractères (string, en anglais) sur une interface ou une console interactive. Dans notre exemple, une tâche est représentée par son titre.

from django.db import models

class Task(models.Model):
	title = models.CharField(max_length=250)
	completed = models.BooleanField(default=False)
	created = models.DateTimeField(auto_now_add=True)

	def __str__(self):
		return self.title

Le système de migration

Makemigrations

Il nous faut maintenant créer la table correspondant au modèle, qui n’existe pas encore dans la base de données. Pour cela, Django utilise un système de migration qui permet de modifier la base de données en accord avec les changements apportés aux modèles.

On génère la migration à l’aide de la commande python manage.py makemigrations, comme ceci :

(venv) D:\DEV\ToDoAppProject\todoapp>python manage.py makemigrations
Migrations for 'todolist':
  todolist\migrations\0001_initial.py
    - Create model Task

Django compare la base de données et les modèles qui la représentent. Lorsqu’il trouve des différences, Django génère un fichier (dans notre cas le fichier 0001_initial.py) au niveau d’un répertoire nommé migrations, se trouvant dans le répertoire de l’application todolist. Le fichier généré va contenir toutes les modifications opérées sur le modèle.

NOTE Le système de migration permet de suivre les changements apportés au modèle pour pouvoir mettre à jour la base de données en conséquence.

Le fichier de migration devrait ressembler à ceci :

# Generated by Django 3.1.7 on 2021-02-23 16:53

from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Task',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('title', models.CharField(max_length=250)),
                ('completed', models.BooleanField(default=False)),
                ('created', models.DateTimeField(auto_now_add=True)),
            ],
        ),
    ]

Migrate

On doit ensuite rendre la migration effective, de sorte que la base de données corresponde aux modèles de données. Pour ce faire, on applique les migrations avec la commande python manage.py migrate comme ceci :

(venv) D:\DEV\ToDoAppProject\todoapp>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, todolist
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK
  Applying todolist.0001_initial... OK

Avec la commande migrate, on exécute les migrations pour l’ensemble des applications de la liste INSTALLED_APPS du fichiers de configuration settings.py. Cette commande permet de synchroniser la base de données avec les modèles.

NOTE A chaque fois que l’on portera une modification au fichier models.py de notre application, on créera une nouvelle migration, via la commande makemigrations, pour que Django enregistre les changements apportés au modèle. Puis on synchronise la base de données avec notre modèle à l’aide de la commande migrate.

Manipuler les données

Comme nous l’avons dit plus haut, l’ORM de Django est une API d’abstraction qui permet d’interagir avec la base de données de notre application. L’ORM permet de sélectionner ou extraire les items de la base de données via des QuerySets.

Un QuerySet correspond à une collection d’objets de la base de données et peut comporter des filtres paramétrables pouvant réduire les résultats des requêtes.

INFO L’ORM de Django nous permet de manipuler les objets des modèles correspondant aux enregistrements de la base de données, sans avoir à nous soucier de la syntaxe SQL spécifique au Système de Gestion de la Base de Données (SGBD) de l’application.

Pour l’instant, nous allons d’abord interagir avec la base de données à travers la console interactive de Django et pour y accéder on utilise la commande python manage.py shell comme ceci :

(venv) D:\DEV\ToDoAppProject\todoapp>python manage.py shell
Python 3.7.4 (tags/v3.7.4:e09359112e, Jul  8 2019, 20:34:20) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 

Pour interagir avec la base de données, il faut en premier lieu importer notre modèle Task.

>>> from todolist.models import Task

Nous allons maintenant voir les opérations que nous pouvons faire pour interagir avec notre base de données.

Les opérations CRUD

Dans la console interactive, on peut procéder via l’ORM de Django à quatre types d’opérations de base pour manipuler les données de notre application, à savoir : créer (Create, en anglais), retrouver (Retrieve, en anglais), mettre à jour (Update, en anglais) et supprimer (Delete, en anglais) les items d’une base de données. Ces opérations sont communément appelées les opérations CRUD.

Créer (Create)

La première opération que nous allons faire sera de créer un nouvel objet et de l’enregistrer dans la base de données. On peut faire cela de deux façons différentes.

  • Première méthode

On crée le premier objet avec pour titre “First task” en instanciant notre classe Task et en l’affectant à la variable task. Ensuite on enregistre l’objet créé dans la base à l’aide la méthode save().

>>> task=Task(title="First task")
>>> task.save()
  • Deuxième méthode

On peut aussi créer un objet en une seule ligne de commande à l’aide de la méthode create(). Grâce à la méthode __str()__ que nous avons défini plus haut dans le fichier models.py, la tâche créée est bel et bien représentée par son titre.

>>> Task.objects.create(title="Second Task")
<Task: Second Task>

Retrouver (Retrieve)

Il existe différentes méthodes qui permettent de retrouver ou sélectionner les objets de notre base de données. Pour cela, on construit un QuerySet et ce, en utilisant un Manager du modèle comme source principale de données.

INFO Chaque modèle a un Manager (Gestionnaire, en français) appelé objects. Un QuerySet équivaut au SELECT du langage SQL. Pour extraire des objets, on utilise les méthodes associées au manager du modèle.

  • La méthode all()

Si on veut récupérer tous les objets (toutes les tâches) enregistrés, on utilise la méthode all() qui nous renvoie un QuerySet de tous les objets de la base de données, à savoir les deux tâches que nous avons créées.

>>> Task.objects.all()
<QuerySet [<Task: First task>, <Task: Second Task>]>

Pour faire simple, la requête Task.objects.all() récupère tous (all(), en anglais) les objets (objects, en anglais) de la classe du modèle Task.

  • Les méthodes filter() et exclude()

On peut appliquer des filtres pour restreindre les résultats à des sous-ensembles des objets de notre classe de modèle.

La méthode filter() renvoie les objets répondant aux paramètres de recherche de celle-ci. Par exemple on peut chercher les tâches qui contiennent le mot “task” dans le titre avec le paramètre title__icontains.

>>> Task.objects.filter(title__icontains='Task')
<QuerySet [<Task: First Task>, <Task: Second Task>]>

La méthode exclude() retourne les objets ne répondant pas aux paramètres de recherche renseignés en argument. On peut rechercher les tâches qui ont été réalisées. Bien sûr, l’instruction que nous avons écrite nous renvoie un QuerySet vide car aucune tâche n’a été complétée.

>>> Task.objects.exclude(completed=False)
<QuerySet []>

On peut aussi enchaîner des filtres comme ceci :

>>> Task.objects.exclude(completed=True).filter(title__icontains='Task')
<QuerySet [<Task: First Task>, <Task: My Second Task>]>
  • La méthode get()

Pour extraire un objet unique, on utilise la méthode get().

>>> Task.objects.get(id=1)
<Task: First Task>

Mettre à jour (Update)

Pour modifier un objet, il suffit de récupérer ce dernier avec la méthode get() dans une variable puis affecter la modification à l’attribut voulu de l’objet. Et comme pour la première méthode de création d’un objet, on enregistre la modification avec la méthode save().

>>> task=Task.objects.get(title='First task')
<Task: First Task>
>>> task.title='My First Task'
>>> task
<Task: My First Task>
>>> task.save()
>>> Task.objects.get(id=1)
<Task: My First Task>

Supprimer (Delete)

Pour supprimer un objet, on le sélectionne avec l méthode get() et on le supprime avec la méthode delete().

>>> task=Task.objects.get(id=2)
>>> task
<Task: Second Task>
>>> task.delete()
(1, {'todolist.Task': 1})
>>> Task.objects.all()
<QuerySet [<Task: My First Task>]>
>>> exit()

On sort de la console interactive avec la méthode exit() pour revenir à l’invite de commande. Dans ce qui suivra, nous effectuerons les opérations que nous venons de voir à travers l’interface d’administration de Django.

Site d’administration des données

Pour afficher et gérer les données de notre application, on va pouvoir utiliser l’interface d’administration fournie par Django. Pour y accéder, on doit juste créer un compte utilisateur.

Créer un super-utilisateur

Pour accéder au site d’administration, on doit juste créer un compte utilisateur via la commande python manage.py createsuperuser puis on entre les données demandées.

(venv) D:\DEV\ToDoAppProject\todoapp>python manage.py createsuperuser
Username (leave blank to use 'user'): admin
Email address: [email protected]
Password:
Password (again):
This password is too short. It must contain at least 8 characters.
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

Après la création du compte, on relance le serveur de développement :

(venv) D:\DEV\ToDoAppProject\todoapp>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
February 24, 2021 - 13:58:18
Django version 3.1.7, using settings 'todoapp.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

Ensuite on se connecte à la page de connexion http://127.0.0.1:8000/admin et on entre le nom d’utilisateur et le mot de passe précédemment créés.

Django administration Interface - Login

On accède à la page d’administration et pour l’instant on peut voir les modèles Groups et Users de Django. En cliquant sur Users, on peut voir le compte qu’on a créé.

Django Administration Interface

Pour voir notre modèle Task dans le site d’administration, on doit ajouter les lignes suivantes dans le fichier admin.py du répertoire de notre application todolist :

from django.contrib import admin
from .models import Task
# Register your models here.

admin.site.register(Task)

Ainsi on peut voir notre modèle s’afficher au niveau de site d’administration comme ceci :

Django administration Interface - Add Model

Administrer les modèles

Grace à cette interface on peut aisément lister, sélectionner, créer, modifier ou supprimer des objets.

Django administration Interface - Objects

Pour ajouter une tâche par exemple, on clique sur Add (ajouter), on indique son titre et on clique sur Save (enregistrer). Cela nous renvoie à la page qui liste les tâches avec un message disant que l’objet créé a bien été ajouté.

Django Administration Interface - Add object

Et voilà! Vous êtes arrivés à la fin de la première partie de ce tutoriel. Vous avez vu comment configurer votre projet Django et vous avez pu manipuler les données de votre application. Dans la prochaine partie du tutoriel, nous verrons les Vues, les formulaires, les urls et les Templates.