Vous êtes ici : Accueil / 2011 / Juin / Utilisation de South avec Django

Utilisation de South avec Django

écrit le 06/06/2011 Par Joseph Rozencwajg
Ce tutoriel introduit les concepts de South et son utilisation dans Django afin de simplifier la gestion de la base de données durant le processus de développement d'une application Django.

Introduction

Ce tutoriel introduit les bases de l'utilisation de South. Nous supposerons ici que vous êtes authentifié avec un compte ayant les droits administrateur sur une machine Debian, sur laquelle PostGreSQL et l'adaptateur Psycopg2 sont correctement installés et configurés. Nous utiliserons Mercurial comme système de gestion des versions du code.

Installation

Sous Debian, une simple commande apt-get permet d'installer South : sudo apt-get install python-django-south. Si vous utilisez Pip, pip install south installera la dernière version de la bibliothèque.

Vous pouvez également installer South manuellement, en téléchargeant la dernière version sous forme d'archive .tar.gz depuis le site de South. Après l'avoir décompressée, lancer la commande : sudo python setup.py install.

Test de l'installation

Crééz un projet Django de test : django-admin.py startproject south_project.

Création d'une base de données PostGreSQL :

su postgres
$ psql
postgres=# CREATE DATABASE <database_name> OWNER <utilisateur_psql>;
CREATE DATABASE
postgres=# \q
$ exit

Configuration du projet :

Remplissez la section DATABASES et INSTALLED_APPS du fichier settings.py du projet comme suit :

# /settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'hr_manager',
        'USER': 'josephr',
        'PASSWORD': 'pilotsystems',
        'HOST': '',
        'PORT': '',
    }
}
...
INSTALLED_APPS = (
    'south',
...
)

Création des tables :

Lancez l'ORM de Django afin de créer la table South dans la base de données :

python manage.py syncdb
Creating tables ...

N.B. :  Il est important au lancement de la commande python manage.py syncdb qu'aucune application autre que South n'ait été déclarée dans le fichier settings.py, et ce afin de laisser South gérer les modèles des applications du projet par la suite à la place de l'ORM de Django.

Dans le cas où vous souhaitez convertir une application Django existante vers South, il faudra suivre une procédure différente. Voir ici.

Utilisation de South depuis le shell Django :

python manage.py shell
>>> import south

>>>

Si jusqu'ici vous n'avez pas eu d'erreur, vous êtes prêt à travailler avec South, Django et PostGreSQL .

Sauvegardons l'état initial du projet à l'aide de Mercurial :

hg init .
hg add .
hg commit -m 'Initial commit'

Création de l'application

Voyons maintenant de manière concrète comment utiliser South durant le développement d'une application Django de gestion des employés d'une entreprise.

L'application en question permet au responsable RH de gérer les employés salariés et en freelance de son entreprise. Les employés en freelance peuvent travailler pour plusieurs départements alors que les salariés ne travaillent que dans un seul.

Commençons par créer l'application : python manage.py startapp hr_manager.

Puis créons des modèles initiaux :

# /hr_manager/models.py
from django.db import models
class Employee(models.Model):
    name = models.CharField(max_length = 100)
    department = models.ForeignKey("Department")
class Department(models.Model):
    name = models.CharField(max_length = 100)
class EmployeeHistory(models.Model):
    employee = models.OneToOneField(Employee)
    date_joined = models.DateField()
    marital_status = models.BooleanField()
class Freelancer(models.Model):
    name = models.CharField(max_length = 100)
    departments = models.ManyToManyField(Department)

Il faudra ensuite ajouter 'hr_manager' à la liste des INSTALLED_APPS dans settings.py.

Types et format des migrations

Une migration est une classe contenant deux fonctions : forwards et backwards. La première exécute la migration tandis que la seconde l'annule.

Il existe deux types de migration dont peut hériter cette classe : SchemaMigration (migrations de structure) et DataMigration (migration de données). Dans le cas des migrations de structure, le contenu des fonctions forwards et backwards sont auto-générés, sauf si des champs personnalisés sont utilisés (voir plus bas). Pour les migrations de données, le squelette de la classe est généré mais il est tout le temps nécessaire de les implémenter car il s'agit d'une logique que South ne peut pas deviner.

Nous nous intéresserons aux migrations de données dans un autre tutoriel et nous concentrerons pour l'instant sur les migrations de structure.

Migration de structure initiale

Une fois une première version des modèles de l'applications créés, générons maintenant la première migration de structure.

python manage.py schemamigration hr_manager --initial
Creating migrations directory at '/home/josephr/Projects/test_south/hr_manager/migrations'...
Creating __init__.py in '/home/josephr/Projects/test_south/hr_manager/migrations'...
 + Added model hr_manager.Employee
 + Added model hr_manager.Department
 + Added model hr_manager.EmployeeHistory
 + Added model hr_manager.Freelancer
 + Added M2M table for departments on hr_manager.Freelancer
Created 0001_initial.py. You can now apply this migration with: ./manage.py migrate hr_manager

N.B. : Cette migration est placée dans le dossier 'migrations' de l'application et respecte le format décrit dans la section précédente. Comme toutes les autres migrations, elle pourra donc être versionnée avec le code du projet à l'aide d'un système de gestion tel que Mercurial. C'est pourquoi, il peut être utile de noter le numéro de migrations dans le commentaire d'un commit.

Appliquons cette migration initiale à notre base de données :

python manage.py migrate hr_manager
Running migrations for hr_manager:
 - Migrating forwards to 0001_initial.
 > hr_manager:0001_initial
 - Loading initial data for hr_manager.
No fixtures found.

Enfin, enregistrons une version du code sur le dépôt local Mercurial :

cd ~/Projects/south_project/
hg add .
hg commit -m 'Modeles initiaux. Migration hr_manager : #0001'

Cycles de migrations de structure

A cette première version de l'application, nous souhaitons maintenant ajouter la capacité de savoir qui est le responsable de chaque employé.

Commençons donc par modifier la classe 'Employee' :

# /hr_manager/models.py
from django.db import models
class Employee(models.Model):
    name = models.CharField(max_length = 100)
    department = models.ForeignKey("Department")
    manager = models.ForeignKey("self", blank = True, null = True)
...

Puis créons une migration de structure à l'aide de South :

python manage.py schemamigration hr_manager --auto
 + Added field manager on hr_manager.Employee
Created 0002_auto__add_field_employee_manager.py. You can now apply this migration with: ./manage.py migrate hr_manager

Lançons cette migration :

python manage.py migrate hr_manager
Running migrations for hr_manager:
 - Migrating forwards to 0002_auto__add_field_employee_manager.
 > hr_manager:0002_auto__add_field_employee_manager
 - Loading initial data for hr_manager.
No fixtures found.

Et enfin, sauvegardons dans Mercurial :

cd ~/Projects/south_project/
hg add .
hg commit -m  'Ajout d'un champ manager. Migration hr_manager : #0002'

Ce cyle sera répété autant de fois que nécessaire.

Retours en arrière (Rollbacks)

L'intérêt majeur des migrations, hormis le fait de pouvoir manipuler la structure et le contenu d'une base de donnéés en Python et de pouvoir installer la dernière version d'une application rapidement et aisément, c'est de pouvoir revenir en arrière durant la phase de développement sans avoir à recréer ou vider la base de données.

De manière similaire à un système de gestion des versions du code, South fournit via les migrations un mécanisme qui permet de revenir vers n'importe quelle version sauvegardée du schema d'une base de données.

Imaginons qu'une troisième migration et qu'un troisième commit aient été générés afin de sauvegarder le changement suivant dans la classe Employee :

/hr_manager/models.py
class Employee(models.Model):
    name = models.CharField(max_length = 100)
    department = models.ForeignKey("Department")
    manager = models.ForeignKey("self", blank = True, null = True)
    age = models.IntegerField()
...

Admettons que le jour suivant ce commit, il soit décidé que l'âge d'un employé n'est plus une donnée qu'il est nécessaire de stocker en base de données. Comment revenir en arrière afin de poursuivre le développement ? Dans un cas comme celui-ci, il serait trivial de simplement effacer la ligne ajoutée à la classe, mais imaginons qu'il s'agisse de modifications plus complexes.

La première étape consisterait à retrouver le numéro de changeset et de migration correspondant à l'état vers lequel nous souhaitons retourner, en listant les commits effectués.

hg log
changeset:   3:4bb2dfa8a1d5
tag:         tip
user:        Joseph Rozencwajg <josephr@pilotsystems.net>
date:        Mon May 30 11:01:15 2011 +0200
summary:     Ajout d'un champ age. Migration hr_manager : #0003
changeset:   2:723e13aa0ac9
user:        Joseph Rozencwajg <josephr@pilotsystems.net>
date:        Wed May 25 12:53:04 2011 +0200
summary:     Ajout d'un champ manager. Migration hr_manager : #0002
...

Comme nous pouvons le voir ici, il s'agit du commit 2 et de la migration 2 (mais ces numéros auraient pu être différents, c'est pourquoi il est important de les noter).

Puis, retournons vers cet état dans la base de données :

python manage.py migrate hr_manager 0002
 - Soft matched migration 0002 to 0002_auto__add_field_employee_manager.
Running migrations for hr_manager:
 - Migrating backwards to just after 0002_auto__add_field_employee_manager.
 < hr_manager:0003_auto__add_field_employee_age

Et dans le code :

hg update 2
3 files updated, 0 files merged, 0 files removed, 0 files unresolved

Aller plus loin

La documentation de South (en anglais) vous permettra de poursuivre et d'aborder l'écriture de migrations de données en utilisant l'API South et des champs Django personnalisés ainsi que la conversion d'une application Django vers South.

Actions sur le document