Le projet Django a été créé à partir d'applications utilisées tous les jours par une équipe de développement dans la ville de Lawrence, Kansas, USA, pour propulser le site du journal de la ville. Tout a commencé à l'automne 2003, quand les programmeurs web Adrian Holovaty et Simon Willison ont commencé à utiliser Python à la place de PHP pour répondre aux multiples demandes des journalistes et éditeurs du journal. C'est la meilleure approche qu'ils aient trouvée pour répondre « en temps et en heure » aux demandes « urgentes et prioritaires ». À l'été 2005, les outils développés étaient devenus une collection conséquente d'outils. Ils pouvaient maintenant permettre à tous les sites du journal de fonctionner sans problème et être modifiés rapidement pour répondre aux besoins des journalistes. Les développeurs, dans une équipe qui avait accueilli désormais Jacob Kaplan-Moss, ont décidé de libérer le code source sous licence BSD et de le nommer Django, en hommage au fameux guitariste de jazz manouche Django Reinhardt. Depuis, même si les développeurs principaux (Adrian et Jacob) contrôlent l'évolution du framework, le projet est développé par une communauté toujours plus importante de développeurs. La première version stable (1.0.4) est sortie en septembre 2008 et Django a commencé à devenir populaire avec la version 1.4 sortie en 2012, première version LTS (Long Term Support) réellement déployée à grande échelle. Le cycle de développement est très rapide avec environ une version mineure par semaine (1.8.16, 1.8.17…) et une version majeure tous les 9 mois (environ) [1]. Cet article sera basé sur la version 1.11, dernière version LTS qui supporte encore Python 2.7, maintenue jusqu'en avril 2020.
1. Mise en place rapide
La grande popularité de Django, en parallèle de l'utilisation de plus en plus importante de Python, tient à la simplicité de mise en œuvre initiale. En quelques commandes, nous aurons un site web fonctionnel comprenant une première page d'accueil, une interface d'administration et la gestion très fine des permissions d'accès aux éléments présents dans la base de données. Pour mettre en place ce site, nous allons utiliser la version 1.11, la dernière version qui va supporter Python 2.7 avant le grand saut vers le tout Python 3. C'est le moment de tester nos dernières anciennes lignes de code avant de faire la conversion de tous les éléments et de vous informer sur les changements qu'il faudra apporter [2].
Ayant régulièrement le besoin de créer différents projets, il est utile de cloisonner chacun d'entre eux pour éviter les conflits de versions qui pourraient se présenter sinon. L'écriture de cet article a été réalisée sous Ubuntu 16.04 LTS à jour et, sauf indication contraire, les logiciels utilisés sont ceux de la distribution. Nous allons dans un premier temps créer l'environnement virtuel qui va contenir les dépendances Python et y installer la dernière version de Django :
$ virtualenv demonstration_django
$ source demonstration_django/bin/activate
$ pip install django==1.11
Il faut maintenant commencer le projet qui va nous occuper durant le reste de l'article, à savoir « monSuperSite ». La commande qui permet de créer ce site est startproject. Attention, nous allons créer une arborescence à côté ce celle de l'environnement virtuel, pour ainsi séparer l'environnement virtuel du projet, nous verrons l’intérêt plus loin :
$ django-admin.py startproject monSuperSite
$ tree -L 2 -d
├── demonstration_django
│ ├── bin
│ ├── include
│ ├── lib
│ ├── local
│ └── share
└── monSuperSite
└── monSuperSite
Le projet « monSuperSite » contient maintenant un sous-répertoire du même nom qui va contenir les informations principales du site. Ce répertoire va servir à régler les accès au site, puis à orienter les requêtes par URL de l'utilisateur. L'environnement minimal nécessaire pour Django est installé, nous allons maintenant personnaliser le site pour lui faire afficher des informations plus intéressantes.
2. Utilisation rapide du modèle MTV
La gestion des données par Django suit le principe « Model-Template-View », dérivé du très connu « Modèle-Vue-Contrôleur » popularisé par Trygve Reenskaug lors de sa visite au Palo Alto Center [3]. Le contrôleur est Django lui-même, le modèle est transformé en tables SQL via l'ORM (Object Relational Mapping) intégré et la vue est une page HTML enrichie de variables propres à Django, ou qui peuvent être basées sur Jinja 2 [4].
L'accès à une donnée du projet se fait pour le visiteur du site via l'accès à une page selon son URL. Nous allons voir ensemble comment créer une page d'accueil pour notre super site. Le routage des URL se situe tout d'abord dans le fichier monSuperSite/urls.py. Les pages à afficher sont déduites à partir d'expressions régulières. Nous allons commencer à explorer son fonctionnement. Il faut lancer Django sur notre nouveau site :
(demonstration_django) $ cd monSuperSite
(demonstration_django) $ python manage.py makemigrations
No changes detected
(demonstration_django) $ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
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 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 sessions.0001_initial... OK
(demonstration_django) $ python manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
April 21, 2017 - 10:30:19
Django version 1.11, using settings 'monSuperSite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Il faut synchroniser les tables préexistantes de Django pour l'authentification des sites et la gestion des permissions avec les commandes makemigrations et migrate . Nous reviendrons sur leur utilisation plus tard. Le site est maintenant vide, mais fonctionnel (voir figure 1).
Le prompt de l'environnement virtuel (demonstration_django) ne sera plus répété par la suite.
Fig. 1 : Page initiale de tout projet Django bien commencé, avec les indications pour créer le reste.
Le fichier urls.py permet à Django de faire la correspondance entre une URL et une fonction Python, de Django ou que vous allez écrire. La mise en place rapide du site a prérempli le fichier avec un routage vers l'interface d'administration fournie par défaut :
urlpatterns =[
url(r'^admin/', admin.site.urls),
]
Quand vous vous rendez sur le site http://localhost:8000/admin, l'interface d'administration est présentée. L'objectif de cet article n'est pas de présenter en détail cette puissante interface, pensez simplement à mettre rapidement un mot de passe non trivial avec la commande :
$ python manage.py createsuperuser
Notez que vous pouvez laisser la commande python manage.py runserver en place, Django analysera en temps réel le code que vous êtes en train d'écrire, ce qui vous permet de vérifier que tout fonctionne (ou pas) tout de suite. Nous allons maintenant améliorer la page d'accueil du site, et indiquer à Django où aller la chercher. Nous allons commencer par modifier le fichier urls.py pour faire un rendu simple vers un fichier html comme suit (attention à l'indentation en Python) :
from django.views.generic.base import TemplateView
urlpatterns =[
url(r'^admin/', admin.site.urls),
url(r'^$', TemplateView.as_view(template_name='index.html'), name='accueil'),
]
Il faut maintenant créer un répertoire templates dans le répertoire monSuperSite et y créer le fichier index.html qui contiendra le minimum « vital » :
$ cd monSuperSite
$ mkdir templates
$ cd templates
$ vi index.html
Le contenu du fichier index.html se compose de balises HTML standards et de définitions spécifiques pour Django. Dans le cas du fichier d'index ci-dessous, ces étiquettes serviront à définir une zone spécifique de contenu avec la commande {% block content %}, ce qui va permettre de changer uniquement le contenu du cœur de la page. Ce fichier minimum est le meilleur endroit pour faire les inclusions de code JavaScript et CSS, afin de modifier très rapidement l'aspect d'un site sans modification intrusive à différents endroits. Dans le code ci-après, le populaire framework Bootstrap est incorporé en utilisant le code fourni en exemple dans la documentation :
<!doctype html>
<htmllang="en">
<head>
<metacharset="utf-8"><!-- Latest compiled and minified CSS -->
<linkrel="stylesheet"href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"crossorigin="anonymous">
<!-- Optional theme -->
<linkrel="stylesheet"href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css"integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp"crossorigin="anonymous">
</head>
<body>
This is the starting page of the project.
<divid="content">
{% blockcontent%}{% endblockcontent%}
</div>
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</body>
</html>
Pour que Django sache où trouver ce fichier, il faut aussi lui indiquer que monSuperSite est une application du projet, il faut donc surcharger dans le fichier settings.py la liste INSTALLED_APPS comme suit :
INSTALLED_APPS =[
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'monSuperSite',
]
Ces étapes préliminaires réalisées, notre site est maintenant fonctionnel, nous allons pouvoir le transformer et nous concentrer sur le contenu à présenter à l'utilisateur.
3. Conception de l'application et des dépendances
L'étape la plus complexe dans la mise en place d'une application, quelle qu'elle soit, est d'établir les relations qui vont relier les différentes tables. L’intérêt pour un utilisateur de Django, c'est qu'il va pouvoir se concentrer principalement sur ce qu'il veut afficher, et non pas sur comment il veut construire les tables de son application selon la base de données qu'il aura besoin d'utiliser. Cette flexibilité est particulièrement déroutante lorsque l'on a l'habitude de concevoir une application de A à Z en établissant une bonne fois pour toutes les relations entre les données. Dans notre cas, nous allons les implémenter pas à pas pour arriver à une application fonctionnelle, riche, sans optimisation préalable.
Nous allons concevoir une petite application de gestion de correspondances entre des protéines, leur structure tridimensionnelle et les ligands connus qui pourraient y être associés. Ce modèle à trois partenaires nous permettra de mettre en place plusieurs types de relations entre les tables et pourrait tout aussi bien être transposé à un modèle d’e-commerce qui relierait produits, clients et fournisseurs.
Rappels de biologie
Les protéines sont composées de briques élémentaires appelées acides aminés. Ces briques sont reliées entre elles pour former une chaîne polypeptidique qui se replie sur elle-même en trois dimensions pour arriver à une architecture stable. De cette architecture découlent leur fonction et leur activité. Si les informations de structure tridimensionnelle sont disponibles, il est possible de concevoir des inhibiteurs dédiés de leur fonction, ce qui peut amener après un long processus de validation (supérieur à 10 ans…) à la mise sur le marché de nouveaux médicaments. Le site présenté ici permet de mettre rapidement en place un système d'information sur les données publiques disponibles pour chacune des protéines.
Pour faciliter l'accès aux informations qui seront enregistrées dans les tables, pour permettre leur édition ou leur incorporation, nous allons gérer l'authentification des utilisateurs, et leur définir un rôle spécifique. Nous allons ainsi créer une application dédiée pour l'authentification qui se basera sur le modèle d'utilisateur de Django. Cette pratique assez courante permet de rajouter facilement des champs spécifiques tout en conservant la facilité de gestion de l'authentification fournie par défaut dans Django.
3.1 L'application utilisateur améliorée : curators
Un travail important dans ce monde moderne consiste à nettoyer l'information, l'organiser, la rendre intelligible. En anglais, ce travail est nommé la curation et jusqu'à ce que l'intelligence artificielle ne les remplace, c'est un travail de fourmi très important. Pour quantifier la qualité de ces curateurs, nous allons créer une application dédiée qui permettra de noter ces curateurs avec un score. Il faut créer une application dédiée et modifier les modèles nécessaires :
$ cd ~/monSuperSite
$ python manage.py startapp curators
$ cd curators
$ vi models.py
Le fichier models.py contient les classes qui seront intégrées dans le projet monSuperSite :
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
from django.contrib.auth.models importUser
from django.core.validators import MinValueValidator, MaxValueValidator
class Curator(models.Model):
user= models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
score = models.IntegerField(default=0,
validators=[MinValueValidator(0),
MaxValueValidator(5)])
birth_date = models.DateField(null=True, blank=True)
Pour que le nouveau modèle créé soit présent dans l'interface d'administration de Django, il faut modifier le fichier monSuperSite/settings.py pour ajouter l'application curators à la liste INSTALLED_APPS comme vu précédemment, mais aussi compléter le fichier admin.py de l'application avec a minima les instructions suivantes :
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.contrib import admin
# Register your models here.
from curators.models import*
admin.site.register(Curator)
Ces fichiers étant créés, il faut synchroniser les tables pour que les changements soient pris en compte, en faisant appel aux migrations.
$ python manage.py makemigrations
Migrations for 'curators':
curators/migrations/0001_initial.py
- Create model Curator
(demonstration_django)stephane@maison:~/monSuperSite$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, curators, sessions
Running migrations:
Applying curators.0001_initial... OK
Après ces modifications, l'interface d'administration présente une nouvelle application qui ne contient pour le moment que l'objet Curator (voir figure 2).
Fig. 2 : L'interface d'administration contient la nouvelle application qui vient d'être ajoutée au système. Il suffit de cliquer sur le bouton Add pour ajouter un nouveau curateur au système, sans intervention additionnelle sur les tables ou la création de pages dédiées.
Dans le cadre de cette présentation rapide de l'authentification, nous nous contenterons de l'interface d'administration principale de Django. Si vous voulez contrôler plus finement la gestion des utilisateurs et leur création en dehors de l'interface d'administration, des explications plus complètes sont disponibles sur les sites donnés en référence [5], avec un code disponible sous GitHub [6][7].
3.2 L'application « protéines »
L'application « protéines » va contenir quelques éléments nécessaires à la caractérisation d'une protéine : son nom, sa fonction, sa séquence en acides aminés. Pour la plupart de ces informations, il est possible de récupérer directement les éléments en utilisant une fiche normalisée, mise à jour mensuellement et validée par des scientifiques experts. Nous allons tout d'abord créer l'application « protéines » avant de définir quels sont les champs nécessaires, afin de présenter l'intérêt de l'utilisation de Django. Si vous voulez utiliser une application basée sur du e-commerce, il vous suffit de changer l'application « protéines » en application « produits ». Pour créer l'application, il faut se situer dans le répertoire monSuperSite, le premier créé au début de cette présentation, et créer l'application :
$ python manage.py startapp protein
Dans le répertoire protein vous trouvez maintenant 5 fichiers Python :
- admin.py : sert à définir la présentation des informations de l'application dans l'interface d'administration de Django ;
- apps.py : sert à définir des classes génériques pour l'application ;
- models.py : fichier qui va contenir les définitions des classes qui seront transformées en tables par la suite ;
- tests.py : vous pourrez créer des tests unitaires qui serviront à valider le bon fonctionnement de l'application lors de changements futurs ;
- views.py : c'est dans ce fichier que nous pourrons créer les vues spécifiques de l'application.
Nous allons créer dans le fichier models.py la classe qui va servir à stocker en base de données les éléments nécessaires pour une protéine donnée. Cette classe sera nommée Protein. Pour que l'administrateur puisse y avoir accès dans l'interface d'administration, il suffira de modifier le fichier admin.py en référençant la classe Protein comme nous l'avons fait pour la classe Curators.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class Protein(models.Model):
name = models.CharField(max_length=255)
description = models.CharField(max_length=1000)
gene = models.CharField(max_length=100,default='NOGENENAME')
mw = models.PositiveIntegerField(blank=True,default=0)
def__unicode__(self):
return u'%s'%self.name
Voici quelques remarques concernant ce fichier models.py : tout d'abord, le fichier commence par la déclaration d'encodage. En Python 2.7, les chaînes de caractères ne sont pas par défaut en UTF-8 donc sans cette précaution le moindre accent qui apparaîtrait dans un commentaire entraînerait une erreur.
Notre classe hérite de la classe Model de Django, c'est cela qui permet d'avoir la cohérence d'ensemble de l'application. La méthode privée __unicode__() n'est utile que pour afficher le nom de l'objet dans l'interface d'admin. Il n'y a pas besoin de spécifier de clé primaire, elle sera générée automatiquement pour nous avec un champ s'auto-incrémentant.
Contrairement à la déclaration d'un objet Python classique, il n'y a pas besoin de déclarer une méthode __init__() qui servirait à définir les champs de l'objet. Nous pouvons directement créer les champs à partir de définitions explicites (CharField, PositiveIntegerField, etc.) héritées de la classe models. L'ensemble de ces champs est très bien détaillé dans la documentation officielle (en français, et à jour) [8].
Après avoir modifié ces deux fichiers, il faut maintenant indiquer à Django qu'une nouvelle application fait partie du projet. Comme précédemment, cela veut dire compléter la liste des applications dans le fichier settings.py. Une fois cet ajout effectué, il faut effectuer la migration de l'application :
$ python manage.py makemigrations
Migrations for 'protein':
protein/migrations/0001_initial.py
- Create model Protein
(demonstration_django)stephane@maison:~/monSuperSite$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, curators, protein, sessions
Running migrations:
Applying protein.0001_initial... OK
Il nous suffit maintenant de créer une vue associée et de l'appeler à partir du fichier urls.py. Comme l'application permet d'isoler les éléments indépendants les uns des autres dans les tables, nous allons continuer ce principe en créant un fichier urls.py dans le répertoire protein, mais aussi un répertoire template qui contiendra les informations spécifiques pour les protéines. Le fichier urls.py sera appelé à partir du fichier urls.py principal présent dans le sous-répertoire monSuperSite, la liste des chemins de recherche sera complétée comme suit :
...
url(r'protein/', include('protein.urls')),
]
Le fichier protein/urls.py qu'il faut créer contient le code suivant :
from django.conf.urls import include,url
from protein.views import*
urlpatterns =[
url(r'(?P<identifiant>\d+)', one_protein, name="one_protein")
]
Quand un utilisateur fait appel à l'URL /protein/1, le premier fichier urls.py redirige vers l'application protein et le second fichier urls.py redirige la requête vers la fonction one_protein() présente dans le fichier views.py. Le paramètre 1 est récupéré avec une expression régulière nommée, le chiffre sera ainsi passé à la méthode one_protein() avec le paramètre identifiant. Le fichier views.py de l'application contient une fonction unique qui sert à récupérer l'objet dans la base à partir de cet identifiant, il contient :
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render, get_object_or_404
from protein.models import Protein
# Create your views here.
def one_protein(request, identifiant):
my_protein=get_object_or_404(Protein, pk=identifiant)
return render(request,"one_protein.html",{'protein':my_protein})
Notez que par rapport au fichier initialement créé, nous importons la fonction get_object_or_404(). Cette fonction utilisée en ligne 10 permet de faire une requête dans la base de données et si celle-ci échoue alors une page 404 est présentée. Tant que le paramètre DEBUG = True est présent dans le fichier settings.py, l'erreur s'affiche dans le navigateur, mais en production il faudra prévoir une vraie page 404 ou une redirection vers une page générique.
Il ne reste plus qu'à créer le template one_protein.html dans un répertoire templates de l'application « protéine ». Un peu de mise en forme est effectué avec Bootstrap, mais il ne s'agit que d'une invitation à faire mieux…
{% extends"index.html"%}
{% blockcontent%}
<divclass="col-md-12">
<h1>Protein detail</h1>
<dlclass="dl-horizontal">
<dt>Protein: </dt><dd>{{ protein.name }}</dd>
<dt>Description: </dt><dd>{{ protein.description }}</dd>
<dt>Gene:</dt><dd> {{ protein.gene }}</dd>
</dl>
</div>
{% endblock %}
Pour compléter un peu les données, nous allons nous servir de la fiche UNIPROT de la protéine P53 (http://www.uniprot.org/uniprot/P04637). Il suffit de se rendre dans l'interface d'administration pour remplir la fiche rapidement en copiant-collant les informations. Une fois ces informations entrées, la page faisant appel à l'application que nous venons d'écrire s'affiche avec les informations nécessaires (voir figure 3).
Fig. 3 : Une première vue complète capable de traiter de manière automatique l'information présente dans la table. L'image présente le résultat de la requête sur la première protéine de la base, P53, et une partie des informations qui sont associées à la fiche.
3.3 Les relations à plusieurs : deux applications supplémentaires
Plutôt que de concentrer nos efforts sur la mise en forme avancée du site (pour le moment), nous allons plutôt ajouter du contenu. L'intérêt d'un site contenant des informations sur des protéines (ou des produits) est qu'il doit être possible de relier ces protéines (ces produits) à d'autres éléments, par exemple à une drogue connue (une molécule pharmaceutique à activité quantifiée), mais aussi aux données tridimensionnelles disponibles sur la protéine. Pour simplifier la gestion des données, mais aussi pour présenter deux cas différents d'utilisation, nous allons supposer que chacune de nos applications aura une relation différente avec la table Protein. La première application va concerner le lien entre une structure tridimensionnelle et la protéine, il s'agira d'une relation n à n (plusieurs protéines peuvent être liées à plusieurs structures), nous l'appellerons « molécule ». L'autre application concerne les ligands, nous allons simplifier la relation avec une protéine à une relation de type 1 à n, une protéine pourra posséder plusieurs ligands.
3.3.1 L'application « molécule »
L'application est démarrée comme auparavant avec startapp, et ajoutée dans le fichier settings.py. Le fichier models.py est construit en indiquant quelques champs pour le code PDB (à quatre lettres), la chaîne (une lettre) et le nom de la structure. Les informations sont issues du site http:/www.rcsb.org.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class Molecule(models.Model):
pdb_code=models.CharField(max_length=4,blank=True)
pdb_chain=models.CharField(max_length=1,blank=True)
description=models.CharField(max_length=255,blank=True)
Il faut ensuite synchroniser les données du projet avec python manage.py makemigrations et python manage.py migrate. Pour l'instant, les applications sont indépendantes et la relation n'est pas établie entre les deux modèles. Comme nous voulons avoir une relation de n à n entre protéines et molécules, nous allons créer cette relation en y faisant référence à l'aide du mot-clé models.ManyToManyField()dans le modèle molecule :
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
from protein.views import Protein
class Molecule(models.Model):
pdb_code=models.CharField(max_length=4,blank=True)
pdb_chain=models.CharField(max_length=1,blank=True)
description=models.CharField(max_length=255,blank=True)
prot=models.ManyToManyField(Protein)
def__unicode__(self):
return u'%s_%s'%(self.pdb_code,self.pdb_chain)
Il faut noter dans ce fichier qu'il faut faire l'import du modèle de l'application « protéine » pour faire la correspondance entre les deux éléments, puis ajouter la relation de plusieurs à plusieurs via l'entrée « prot ». Contrairement à la conception d'une application où les tables doivent être construites une par une, il n'y a pas besoin de se préoccuper de la table intermédiaire qui sert à faire les correspondances, c'est Django qui a créé cette table de manière automatique. Pour enregistrer cette nouvelle relation, il faut procéder aux migrations des tables :
$ python manage.py makemigrations
Migrations for 'molecule':
molecule/migrations/0002_molecule_prot.py
- Add field prot to molecule
(demonstration_django)stephane@stephane-VirtualBox:~/monSuperSite$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, curators, molecule, protein, sessions
Running migrations:
Applying molecule.0002_molecule_prot... OK
La traduction de cette relation est visible directement dans l'interface d'administration, où il faut maintenant indiquer lors de la création d'une entrée « molecule » la relation qui doit exister avec une entrée « protein » en la sélectionnant par simple clic (voir figure 4).
Fig. 4 : L'ajout de l'entrée 1AIE comme nouvelle molécule se fait dans l'interface d'administration, mais l'établissement d'une relation de plusieurs à plusieurs se voit sous forme de liste d'entrées à choisir ou à ajouter en cliquant sur l'icône « + ».
3.3.2 L'application « ligand »
Nous allons cette fois-ci créer une application « ligand » qui doit aussi être référencée dans le fichier settings.py. La contrainte entre les deux tables sera maintenant réalisée grâce à la méthode models.ForeignKey(). Le site pubchem (https://pubchem.ncbi.nlm.nih.gov/) est une des sources de référence pour les ligands chimiques. Ces molécules peuvent parfois avoir un nom commun qui correspond à des médicaments disponibles en pharmacie, nous utiliserons donc un champ pour le nom chimique, et un autre nom pour le « nom commun » afin de prendre en compte les deux informations :
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
from protein.models import Protein
class Ligand(models.Model):
pubchem_id=models.PositiveIntegerField(blank=True)
common_name=models.CharField(max_length=255)
chemical_name=models.CharField(max_length=255)
p=models.ForeignKey(Protein)
def__unicode__(self):
return u'%s'%self.common_name
Si vous avez modifié tous les fichiers nécessaires, la migration se passe sans encombre :
Migrations for 'ligand':
ligand/migrations/0001_initial.py
- Create model Ligand
(demonstration_django) stephane@maison:~/monSuperSite$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, curators, ligand, molecule, protein, sessions
Running migrations:
Applying ligand.0001_initial... OK
Nous avons maintenant mis en place plusieurs tables contenant différents types de relation, il est temps d'exploiter ces relations pour présenter une information enrichie à l'utilisateur de notre site web.
4. Mise à jour de la vue protéine
Nous avions commencé par écrire une vue simple des actions disponibles dans la conception de la première application, avec la création d'un template dédié à la présentation de l'information. Nous pourrions de même créer un template dédié par application pour présenter le détail des objets qui y sont référencés, mais cela ne serait pas utile pour la présentation du service. Nous allons maintenant reprendre la première vue avec une présentation un peu plus complète (un peu plus de code Bootstrap et jQuery) du site, l'aspect étant toujours résolument austère.
Notre fichier proteine/views.py va maintenant être modifié pour transmettre au template les informations concernant les ligands et les molécules associées. Pour avoir l'affichage de quelques résultats, les ligands clofibrate et doxorubicine et la structure PDB 1YC5_B ont été rajoutés via l'interface d'admin. Pour obtenir la liste des éléments reliés à notre protéine, il suffit de trier les éléments qui lui sont associés avec l'attribut _set.all(). Cette abstraction permet l'utilisation d'une syntaxe identique pour obtenir les objets liés à l'objet protein principal sans qu'il ne soit nécessaire de se soucier du type de relation sous-jacent entre les modèles.
Le fichier views.py devient maintenant :
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render, get_object_or_404
from protein.models import Protein
# Create your views here.
def one_protein(request, identifiant):
my_dict={}
my_protein=get_object_or_404(Protein, pk=identifiant)
my_dict['protein']=my_protein
my_ligands=my_protein.ligand_set.all()
my_dict['ligands']=my_ligands
my_molecules=my_protein.molecule_set.all()
my_dict['molecules']=my_molecules
return render(request,"one_protein.html",my_dict)
Le fichier template one_protein.html est enrichi pour prendre en compte les nouveaux éléments :
{% extends"index.html"%}
{% blockcontent%}
<divclass="col-md-12">
<h1>Protein details</h1>
<dlclass="dl-horizontal">
<dt>Protein: </dt><dd>{{ protein.name }}</dd>
<dt>Description: </dt><dd>{{ protein.description }}</dd>
<dt>Gene:</dt><dd> {{ protein.gene }}</dd>
</dl>
</div>
<div class="col-md-12">
<h1>Ligands binding to this protein</h1>
<ul>
{%forl in ligands %}
<li><a href="https://pubchem.ncbi.nlm.nih.gov/compound//{{l.pubchem_id}}">{{l.common_name}}</a></li>{% endfor %}
</ul>
</div>
<divclass="col-md-12">
<h1>Structures associated</h1>
<ul>
{%form in molecules %}
<li><a href="http://www.rcsb.org/pdb/explore/explore.do?structureId=/{{m.pdb_code}}">{{m.description}}</a></li>{% endfor %}
</ul>
</div>
</div>
{% endblock %}
Notre première application est fonctionnelle, elle contient les tables contenant différents types de relation (1 à 1 pour le modèle utilisateur, puis 1 à n et n à n). Le résultat n'est pour l'instant disponible que pour une protéine, mais il est déjà suffisant pour montrer la pertinence de Django pour la mise en forme rapide d'un site web rapide et riche (voir figure 5).
Fig. 5 : La vue initiale comprenant les informations de la protéine, mais aussi les relations qui ont été ajoutées est affichée dans une page synthétique. L'essentiel du travail de traitement des données est effectué au niveau de la vue, mais il est possible d'accéder à de nombreux attributs dans le template sans requête additionnelle, gage de gain de temps, mais aussi de sécurité.
5. Sauvegarder les données et les restaurer
Le travail présenté brièvement ne peut être exploitable qu'avec un minimum de données entrées par l'utilisateur. Pour ne pas surcharger l'article, ces données ont été insérées directement via l'interface d'administration, mais il aurait été tout aussi possible de les insérer après authentification en utilisant le modèle Curator pour ajouter les données dans la base. Nous n'avons pas effectué de grosses modifications avec les migrations - cela sera pour un autre numéro - mais c'est bien ce que permettent les migrations, en ajoutant par exemple un champ à l'objet Protein. Lors des nombreuses manipulations, il est dangereux de ne pas effectuer de copie de ses données, sous peine de perdre le travail en cours. Django nous fournit une interface très pratique pour cela, toujours avec le gestionnaire de projets manage.py. Pour sauvegarder les données, il suffit ainsi de faire :
$ python manage.py dumpdata > fichier.json
Vous pouvez maintenant effectuer les changements nécessaires à l'organisation des tables, ajouter des champs dans chaque modèle, etc. En général les migrations permettent d'éviter les problèmes, mais cela peut arriver suite à une fausse manœuvre… Si cela vous arrivait, il suffit de revenir en arrière en spécifiant la version de migration qui fonctionnait (allez voir dans le répertoire migrations de chacune des applications), et d'importer les données au stade où vous en étiez à ce moment.
$ python manage.py loaddata fichier.json
6. Passage en production
Cet élément est rarement traité, car si vous développez l'application, vous n’êtes probablement pas l'administrateur système qui va s'occuper de cette étape. Comme vu en début d'article, nous avons cherché à cloisonner les versions de Django et de ses dépendances dans un environnement virtuel dédié. Le plus simple est de se baser sur celui-ci pour gérer les dépendances à installer sur le serveur. Pour connaître la liste des paquets installés, il faut exécuter la commande :
$ pip freeze > requirements.txt
Sur l'exemple proposé dans cet article, le résultat est :
appdirs==1.4.3
Django==1.11
packaging==16.8
pkg-resources==0.0.0
pyparsing==2.2.0
pytz==2017.2
six==1.10.0
Vous pouvez maintenant copier le code sur le serveur qui va héberger le site, ou mieux, cloner le dépôt du projet, il n'y aura en effet que quelques étapes à réaliser :
- adapter le fichier settings.py pour définir l'URL pour les fichiers statiques et media ;
- ajouter le nom de la machine dans la liste ALLOWED_HOSTS ;
- passer le drapeau DEBUG = True à False ;
- changer la base de données (passer de Sqlite à MySQL par exemple). Le plus simple est de faire le dump de la base avec Django, de changer les réglages dans le fichier settings.py, et de charger les données ;
- déplacer les fichiers statiques au bon endroit avec la commande python manage.py collectstatic ;
- utiliser le fichier de configuration pour le serveur web, qui traitera les fichiers statiques et utilisera l'environnement virtuel que vous allez recréer.
Pour recréer l'environnement virtuel, il vous suffit sur la seconde machine de lancer à nouveau un virtualenv (dans un espace dédié, indépendant du projet de site) et d'installer les dépendances avec la commande :
$ pip install -r requirements.txt
Pour Apache 2.4, le fichier de configuration suivant présente un exemple directement utilisable dans un hôte virtuel dédié :
<VirtualHost *:80>
ServerName localhost
ServerAlias localhost
# https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/modwsgi/
# Pour monSuperSite
WSGIDaemonProcess monSuperSite python-path=/home/stephane/monSuperSite:/home/stephane/monSuperSite/tools/django1.8/lib/python2.7/site-packages
processes=2 threads=4 display-name=monSuperSite
WSGIProcessGroup monSuperSite
WSGIScriptAlias /monSuperSite '/home/stephane/monSuperSite/monSuperSite/wsgi.py'
# Pour monSuperSite
<Directory /home/stephane/monSuperSite/monSuperSite/>
WSGIProcessGroup monSuperSite
<Files wsgi.py>
Options +ExecCGI -MultiViews +FollowSymLinks
AllowOverride None
Require all granted
</Files>
</Directory>
Alias /monSuperSite/static /home/stephane/monSuperSite/monSuperSite/static/
<Location /monSuperSite/static/>
WSGIProcessGroup monSuperSite
Options -Indexes +FollowSymLinks
Require all granted
</Location>
Alias /monSuperSite/media /home/stephane/monSuperSite/monSuperSite/media/
<Location /monSuperSite/media/>
WSGIProcessGroup monSuperSite
Options -Indexes +FollowSymLinks
Require all granted
</Location>
# Pour séparer les bons logs de l'ivraie
ErrorLog ${APACHE_LOG_DIR}/monSuperSite-error.log
CustomLog ${APACHE_LOG_DIR}/monSuperSite-access.log combined
</VirtualHost>
Vous pourrez trouver plus de détails sur l'intégration complète d'un site tournant sous Django sur mon site [9].
Conclusion
Nous avons pu voir dans cet article le fonctionnement opérationnel de Django, et vu les différences qui existent dans son implémentation lorsqu'il s'agit de mettre en place un site fonctionnel. Au lieu d'utiliser un framework clé en main comme il en existe tant, Django vous permet de vous libérer des vicissitudes de l'utilisation d'une base de données, en l'ajustant à votre problème si nécessaire. Nous n'avons fait que survoler les fonctionnalités, en particulier la conception des tables et leur évolution, à travers un cas simple. Dans des cas plus complexes, Django nous apporte une réelle flexibilité dans le déploiement d'applications en peu de temps, et ce pour un investissement raisonnable. De par la complexité de Django, sa puissance, et son rayonnement, c'est assurément un framework à considérer pour des projets pérennes. Le prototypage d'un site est rapide, surtout en le couplant avec d'autres technologies éprouvées en JavaScript et en CSS. C'est cependant la vitalité de la communauté, la diversité des applications et la richesse des briques logicielles qui en font un des acteurs majeurs du Web de demain.
Références
[1] Roadmap pour les prochaines versions de Django : https://www.djangoproject.com/download/
[2] Différences entre python2 et python3 : https://wiki.python.org/moin/Python2orPython3
[3] L'origine du MVC : https://fr.wikipedia.org/wiki/Modèle-vue-contrôleur
[4] Le langage de templates : https://docs.djangoproject.com/fr/1.11/topics/templates/
[5] Description complète de l'authentification dans Django : https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Authentication
[6] Une explication rapide pour étendre le modèle d'authentification : https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html
[7] Code et explications prêts à l'emploi pour les templates de gestion de l'authentification : http://blog.narenarya.in/right-way-django-authentication.html , https://www.codementor.io/jadianes/build-data-product-django-user-management-du107uyuh
[8] Détail des instances et des options disponibles dans la classe models : https://docs.djangoproject.com/fr/1.11/ref/models/fields/
[9] Introduction au déploiement d'une application de bioinformatique propulsée par Django : http://www.steletch.org/spip.php?article91