Une migration est un script permettant de mettre à jour le schéma de la BDD.
Il est nécessaire de créer des migrations (et les lancer) pour créer les tables associées aux modèles.
Pour créer la ou les migrations permettant de réfleter les changements de modèle, on utilise la commande suivante:
$ python manage.py makemigrations
On obtiendra ce type de fichier:
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Search',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('text', models.CharField(max_length=255, verbose_name='text')),
('weight', models.PositiveSmallIntegerField(default=1, verbose_name='weight')),
],
options={
'ordering': ['-pk'],
'base_manager_name': 'objects',
},
managers=[
('objects', core.models.search.Manager()),
],
),
]
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.AddField(
model_name='search',
name='source',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='searches', to=settings.AUTH_USER_MODEL),
),
]
Notons que pour qu’un modèle soit connu de Django, et donc pris en compte dans les migrations, il faut
que l’application dans laquelle le modèle se trouve soit activée dans les configurations
# Looks for models and locales of installed apps
INSTALLED_APPS = (
...
'lib.study',
)
que la classe (qui hérite de Model) soit présente dans APPNAME.models
— ou importée à partir de ce fichier, ou encore importée par récursion
core/models/init.py:
# flake8: noqa
from .search import Search
et enfin, le répertoire migrations
de l’application doit exister.
S’il n’existe pas déjà, alors passer le label de l’application en paramètre de makemigrations
pour créer le répertoire et la migration initiale
$ python manage.py makemigrations study
Lister les migrations et leur statut (appliquée ou non)
$ python manage.py showmigrations
Vérifier s’il y a des migrations à lancer
$ python manage.py migrate --check
Afficher les requêtes SQL d’une migration
$ python manage.py sqlmigrate core 0001_initial
Pour lancer les migrations:
$ python manage.py migrate
Notons que la liste des migrations appliquées sont sauvegardées en BDD, dans la table django_migrations
.
Seules les migrations dont le nom n’est pas déjà présent dans cette table seront lancées
CreateModel
Permet de créer une table
migrations.CreateModel(
name='Study',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
],
options={
'ordering': ['created_at', 'pk'],
'base_manager_name': 'objects',
},
),
migrations.CreateModel(
name='StudyMedicalBloodTest',
fields=[
('blood_test_id', models.AutoField(primary_key=True, serialize=False)),
],
options={
'abstract': False,
},
bases=(lib.study.choices.study_medical.BloodTestStatusMixin, models.Model),
),
AlterModelOptions
Permet de modifier les options d’un modèle
Notons que certaines options n’aurons aucune incidences sur la BDD, uniquement sur le comportement de Django
migrations.AlterModelOptions(
name='study',
options={
'base_manager_name': 'objects',
'ordering': ['created_at', 'pk'],
},
),
AddField
Permet d’ajouter un champ
migrations.AddField(
model_name='studymcm',
name='context_other_comment',
field=models.TextField(blank=True, max_length=2048, verbose_name='comments'),
),
RenameField
Permet de renommer un champ
migrations.RenameField(
model_name='studymcm',
old_name='date',
new_name='mcm_date',
),
AlterField
Permet de modifier un champ
migrations.AlterField(
model_name='study',
name='id',
field=models.AutoField(primary_key=True, serialize=False),
),
RemoveField
Permet de supprimer un champ
migrations.RemoveField(
model_name='studyradiologicalnodule',
name='density_is_mixed',
),
RunSQL
Permet de lancer une requête SQL tel-que
migrations.RunSQL(
r'''
UPDATE study_studymedical
SET
studymedicalinclusion_ptr_id = study_ptr_id,
studymedicalclinicaldata_ptr_id = study_ptr_id;
'''
),
migrations.RunSQL(
r"""
WITH objects AS (
-- OCS list
SELECT treatment_id, json_agg(data) as data_list
FROM (
-- OCS item: prednisolone
(
SELECT json_build_object(
'vidal_id', 2912,
'quantity', treatment_drugs_ocs_prednisolone_quantity,
'duration', treatment_drugs_ocs_prednisolone_duration
) as data, treatment_id
FROM asthma_initial_asthmainitialrecordtreatment as a
WHERE treatment_drugs_ocs_prednisolone='t'
)
UNION ALL
-- OCS item: prednisone
(
SELECT json_build_object(
'vidal_id', 2913,
'quantity', treatment_drugs_ocs_prednisone_quantity,
'duration', treatment_drugs_ocs_prednisone_duration
) as data, treatment_id
FROM asthma_initial_asthmainitialrecordtreatment as a
WHERE treatment_drugs_ocs_prednisone='t'
)
) x
GROUP BY x.treatment_id
)
UPDATE asthma_initial_asthmainitialrecordtreatment t
SET treatment_drugs_ocs_list = x.data_list
FROM objects x
WHERE t.treatment_id = x.treatment_id
RETURNING t.treatment_id;
"""
),
RunPython
Permet de lancer du code python pendant la migration
import django.core.serializers.json
from django.db import migrations, models
from django.core.management import call_command
def resave_study(apps, schema_editor):
# management command, that can be launched with `python manage.py resave_study`
call_command('resave_study')
class Migration(migrations.Migration):
dependencies = [
('study', '0011_auto_20221220_1809'),
]
operations = [
migrations.RunPython(resave_study, migrations.RunPython.noop),
]
from django.db import migrations, models
def resave_study(apps, schema_editor):
queryset = (
apps
.get_model('study', 'Study')
.objects
.all()
.order_by('patient_id', 'polymorphic_ctype', 'id')
)
patient_id = None
increment = {}
for record in queryset:
if record.patient_id != patient_id:
increment = {}
patient_id = record.patient_id
pid = record.polymorphic_ctype_id
rel_step_number = increment.get(pid, 0) + 1
increment[pid] = rel_step_number
record.rel_step_number = rel_step_number
record.save(update_fields=[
'rel_step_number',
])
class Migration(migrations.Migration):
dependencies = [
('study', '0008_study_rel_step_number'),
]
operations = [
migrations.RunPython(resave_study, migrations.RunPython.noop),
]