Database
 sql >> Base de données >  >> RDS >> Database

Django Migrations :une introduction

Regarder maintenant Ce didacticiel contient un cours vidéo associé créé par l'équipe Real Python. Regardez-le avec le didacticiel écrit pour approfondir votre compréhension :Django Migrations 101

Depuis la version 1.7, Django est livré avec un support intégré pour les migrations de bases de données. Dans Django, les migrations de bases de données vont généralement de pair avec les modèles :chaque fois que vous codez un nouveau modèle, vous générez également une migration pour créer la table nécessaire dans la base de données. Cependant, les migrations peuvent faire beaucoup plus.

Vous allez apprendre comment fonctionnent les migrations Django et comment en tirer le meilleur parti au cours de quatre articles et d'une vidéo :

  • Partie 1 :Migrations Django :introduction (article actuel)
  • Partie 2 :Approfondir les migrations
  • Partie 3 :Migrations de données
  • Vidéo :Migrations Django 1.7 - Introduction

Dans cet article, vous vous familiariserez avec les migrations Django et apprendrez ce qui suit :

  • Comment créer des tables de base de données sans écrire de code SQL
  • Comment modifier automatiquement votre base de données après avoir modifié vos modèles
  • Comment annuler les modifications apportées à votre base de données

Bonus gratuit : Cliquez ici pour accéder à un guide gratuit des ressources d'apprentissage Django (PDF) qui vous montre des trucs et astuces ainsi que les pièges courants à éviter lors de la création d'applications Web Python + Django.


Les problèmes résolus par les migrations

Si vous débutez avec Django ou le développement Web en général, vous n'êtes peut-être pas familiarisé avec le concept des migrations de bases de données, et il peut ne pas sembler évident pourquoi c'est une bonne idée.

Tout d'abord, définissons rapidement quelques termes pour nous assurer que tout le monde est sur la même longueur d'onde. Django est conçu pour fonctionner avec une base de données relationnelle, stockée dans un système de gestion de base de données relationnelle comme PostgreSQL, MySQL ou SQLite.

Dans une base de données relationnelle, les données sont organisées en tables. Une table de base de données a un certain nombre de colonnes, mais elle peut avoir n'importe quel nombre de lignes. Chaque colonne a un type de données spécifique, comme une chaîne d'une certaine longueur maximale ou un entier positif. La description de toutes les tables avec leurs colonnes et leurs types de données respectifs s'appelle un schéma de base de données.

Tous les systèmes de base de données pris en charge par Django utilisent le langage SQL pour créer, lire, mettre à jour et supprimer des données dans une base de données relationnelle. SQL est également utilisé pour créer, modifier et supprimer les tables de base de données elles-mêmes.

Travailler directement avec SQL peut être assez fastidieux, donc pour vous faciliter la vie, Django est livré avec un mappeur relationnel objet, ou ORM en abrégé. L'ORM mappe la base de données relationnelle au monde de la programmation orientée objet. Au lieu de définir des tables de base de données en SQL, vous écrivez des modèles Django en Python. Vos modèles définissent des champs de base de données, qui correspondent aux colonnes de leurs tables de base de données.

Voici un exemple de la façon dont une classe de modèle Django est mappée à une table de base de données :

Mais le simple fait de définir une classe de modèle dans un fichier Python ne fait pas apparaître une table de base de données comme par magie. La création des tables de base de données pour stocker vos modèles Django est le travail d'une migration de base de données. De plus, chaque fois que vous apportez une modification à vos modèles, comme l'ajout d'un champ, la base de données doit également être modifiée. Les migrations gèrent également cela.

Voici quelques façons dont les migrations Django vous facilitent la vie.


Apporter des modifications à la base de données sans SQL

Sans migrations, vous auriez à vous connecter à votre base de données et à saisir un ensemble de commandes SQL ou à utiliser un outil graphique tel que PHPMyAdmin pour modifier le schéma de la base de données chaque fois que vous souhaitiez modifier la définition de votre modèle.

Dans Django, les migrations sont principalement écrites en Python, vous n'avez donc pas besoin de connaître SQL, sauf si vous avez des cas d'utilisation vraiment avancés.



Éviter les répétitions

Créer un modèle puis écrire du SQL pour créer les tables de base de données pour celui-ci serait répétitif.

Les migrations sont générées à partir de vos modèles, en veillant à ce que vous ne vous répétiez pas.



Assurer la synchronisation des définitions de modèle et du schéma de base de données

Habituellement, vous avez plusieurs instances de votre base de données, par exemple une base de données pour chaque développeur de votre équipe, une base de données pour les tests et une base de données avec des données en direct.

Sans migrations, vous devrez effectuer des modifications de schéma sur chacune de vos bases de données, et vous devrez garder une trace des modifications déjà apportées à quelle base de données.

Avec Django Migrations, vous pouvez facilement synchroniser plusieurs bases de données avec vos modèles.



Suivi de la modification du schéma de la base de données dans le contrôle de version

Un système de contrôle de version, comme Git, est excellent pour le code, mais pas tellement pour les schémas de base de données.

Comme les migrations sont du Python simple dans Django, vous pouvez les placer dans un système de contrôle de version comme n'importe quel autre morceau de code.

À présent, vous êtes, espérons-le, convaincu que les migrations sont un outil utile et puissant. Commençons à apprendre à libérer ce pouvoir.




Configuration d'un projet Django

Tout au long de ce didacticiel, vous allez travailler sur une simple application de suivi Bitcoin comme exemple de projet.

La première étape consiste à installer Django. Voici comment procéder sous Linux ou macOS X à l'aide d'un environnement virtuel :

$ python3 -m venv env
$ source env/bin/activate
(env) $ pip install "Django==2.1.*"
...
Successfully installed Django-2.1.3

Vous avez maintenant créé un nouvel environnement virtuel et l'avez activé, ainsi que Django installé dans cet environnement virtuel.

Notez que sous Windows, vous exécuterez env/bin/activate.bat au lieu de source env/bin/activate pour activer votre environnement virtuel.

Pour une meilleure lisibilité, les exemples de console n'incluront pas le (env) partie de l'invite à partir de maintenant.

Avec Django installé, vous pouvez créer le projet en utilisant les commandes suivantes :

$ django-admin.py startproject bitcoin_tracker
$ cd bitcoin_tracker
$ python manage.py startapp historical_data

Cela vous donne un projet simple et une application appelée historical_data . Vous devriez maintenant avoir cette structure de répertoire :

bitcoin_tracker/
|
├── bitcoin_tracker/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
|
├── historical_data/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations/
│   │   └── __init__.py
|   |
│   ├── models.py
│   ├── tests.py
│   └── views.py
|
└── manage.py

Dans le bitcoin_tracker répertoire, il y a deux sous-répertoires :bitcoin_tracker pour les fichiers à l'échelle du projet et historical_data contenant des fichiers pour l'application que vous avez créée.

Maintenant, pour créer un modèle, ajoutez cette classe dans historical_data/models.py :

class PriceHistory(models.Model):
    date = models.DateTimeField(auto_now_add=True)
    price = models.DecimalField(max_digits=7, decimal_places=2)
    volume = models.PositiveIntegerField()

C'est le modèle de base pour suivre les prix du Bitcoin.

N'oubliez pas non plus d'ajouter l'application nouvellement créée à settings.INSTALLED_APPS . Ouvrez bitcoin_tracker/settings.py et ajoutez historical_data à la liste INSTALLED_APPS , comme ceci :

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

Les autres paramètres conviennent à ce projet. Ce didacticiel suppose que votre projet est configuré pour utiliser une base de données SQLite, qui est la valeur par défaut.



Créer des migrations

Une fois le modèle créé, la première chose à faire est de créer une migration pour celui-ci. Vous pouvez le faire avec la commande suivante :

$ python manage.py makemigrations historical_data
Migrations for 'historical_data':
  historical_data/migrations/0001_initial.py
    - Create model PriceHistory

Remarque : En spécifiant le nom de l'application, historical_data , est facultatif. Le laisser désactivé crée des migrations pour toutes les applications.

Cela crée le fichier de migrations qui indique à Django comment créer les tables de base de données pour les modèles définis dans votre application. Reprenons l'arborescence des répertoires :

bitcoin_tracker/
|
├── bitcoin_tracker/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
|
├── historical_data/
│   ├── migrations/
│   │   ├── 0001_initial.py
│   │   └── __init__.py
|   |
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
|
├── db.sqlite3
└── manage.py

Comme vous pouvez le voir, les migrations répertoire contient maintenant un nouveau fichier :0001_initial.py .

Remarque : Vous remarquerez peut-être que l'exécution de makemigrations la commande a également créé le fichier db.sqlite3 , qui contient votre base de données SQLite.

Lorsque vous essayez d'accéder à un fichier de base de données SQLite3 inexistant, il sera automatiquement créé.

Ce comportement est unique à SQLite3. Si vous utilisez une autre base de données comme PostgreSQL ou MySQL, vous devez créer la base de données vous-même avant exécutant makemigrations .

Vous pouvez jeter un coup d'œil à la base de données avec le dbshell commande de gestion. Dans SQLite, la commande pour lister toutes les tables est simplement .tables :

$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
sqlite>

La base de données est toujours vide. Cela changera lorsque vous appliquerez la migration. Tapez .quit pour quitter le shell SQLite.



Application de migrations

Vous avez maintenant créé la migration, mais pour réellement apporter des modifications à la base de données, vous devez l'appliquer avec la commande de gestion migrate :

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, historical_data, 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 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 historical_data.0001_initial... OK
  Applying sessions.0001_initial... OK

Il y a beaucoup de choses ici! Selon la sortie, votre migration a été appliquée avec succès. Mais d'où viennent toutes les autres migrations ?

N'oubliez pas le paramètre INSTALLED_APPS ? Certaines des autres applications répertoriées ici sont également livrées avec des migrations, et le migrate La commande de gestion applique les migrations pour toutes les applications installées par défaut.

Jetez un autre coup d'œil à la base de données :

$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
auth_group                    django_admin_log
auth_group_permissions        django_content_type
auth_permission               django_migrations
auth_user                     django_session
auth_user_groups              historical_data_pricehistory
auth_user_user_permissions
sqlite>

Maintenant, il y a plusieurs tables. Leurs noms vous donnent une idée de leur but. La migration que vous avez générée à l'étape précédente a créé le historical_data_pricehistory table. Inspectons-le en utilisant le .schema commande :

sqlite> .schema --indent historical_data_pricehistory
CREATE TABLE IF NOT EXISTS "historical_data_pricehistory"(
  "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
  "date" datetime NOT NULL,
  "price" decimal NOT NULL,
  "volume" integer unsigned NOT NULL
);

Le .schema commande imprime le CREATE instruction que vous exécuteriez pour créer la table. Le paramètre --indent le formate bien. Même si vous n'êtes pas familier avec la syntaxe SQL, vous pouvez voir que le schéma de historical_data_pricehistory table reflète les champs de PriceHistory modèle.

Il y a une colonne pour chaque champ et une colonne supplémentaire id pour la clé primaire, que Django crée automatiquement à moins que vous ne spécifiiez explicitement une clé primaire dans votre modèle.

Voici ce qui se passe si vous exécutez la commande migrate commande à nouveau :

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
  No migrations to apply.

Rien! Django se souvient des migrations déjà appliquées et n'essaie pas de les relancer.

Il est à noter que vous pouvez également limiter la migrate commande de gestion à une seule application :

$ python manage.py migrate historical_data
Operations to perform:
 Apply all migrations: historical_data
Running migrations:
 No migrations to apply.

Comme vous pouvez le voir, Django n'applique désormais que les migrations pour les historical_data application.

Lorsque vous exécutez les migrations pour la première fois, il est judicieux d'appliquer toutes les migrations pour vous assurer que votre base de données contient les tables nécessaires pour les fonctionnalités que vous pourriez considérer comme acquises, telles que l'authentification des utilisateurs et les sessions.



Changer de modèle

Vos modèles ne sont pas figés. Vos modèles changeront au fur et à mesure que votre projet Django gagnera en fonctionnalités. Vous pouvez ajouter ou supprimer des champs ou modifier leurs types et options.

Lorsque vous modifiez la définition d'un modèle, les tables de base de données utilisées pour stocker ces modèles doivent également être modifiées. Si vos définitions de modèle ne correspondent pas à votre schéma de base de données actuel, vous rencontrerez très probablement une django.db.utils.OperationalError .

Alors, comment modifiez-vous les tables de la base de données ? En créant et en appliquant une migration.

En testant votre tracker Bitcoin, vous réalisez que vous avez fait une erreur. Les gens vendent des fractions de Bitcoin, donc le champ volume doit être du type DecimalField au lieu de PositiveIntegerField .

Modifions le modèle pour qu'il ressemble à ceci :

class PriceHistory(models.Model):
    date = models.DateTimeField(auto_now_add=True)
    price = models.DecimalField(max_digits=7, decimal_places=2)
    volume = models.DecimalField(max_digits=7, decimal_places=3)

Sans migrations, vous auriez à comprendre la syntaxe SQL pour transformer un PositiveIntegerField dans un DecimalField . Heureusement, Django s'en chargera pour vous. Dites-lui simplement de faire des migrations :

$ python manage.py makemigrations
Migrations for 'historical_data':
  historical_data/migrations/0002_auto_20181112_1950.py
    - Alter field volume on pricehistory

Remarque : Le nom du fichier de migration (0002_auto_20181112_1950.py ) est basé sur l'heure actuelle et sera différent si vous suivez votre système.

Vous appliquez maintenant cette migration à votre base de données :

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
  Applying historical_data.0002_auto_20181112_1950... OK

La migration a été appliquée avec succès, vous pouvez donc utiliser dbshell pour vérifier que les modifications ont eu un effet :

$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .schema --indent historical_data_pricehistory
CREATE TABLE IF NOT EXISTS "historical_data_pricehistory" (
  "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
  "date" datetime NOT NULL,
  "price" decimal NOT NULL,
  "volume" decimal NOT NULL
);

Si vous comparez le nouveau schéma avec le schéma que vous avez vu précédemment, vous remarquerez que le type du volume la colonne est passée de integer en decimal pour refléter le changement de volume champ dans le modèle de PositiveIntegerField à DecimalField .



Liste des migrations

Si vous voulez savoir quelles migrations existent dans un projet Django, vous n'avez pas besoin de creuser dans les migrations répertoires de vos applications installées. Vous pouvez utiliser le showmigrations commande :

$ ./manage.py showmigrations
admin
 [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
 [X] 0003_logentry_add_action_flag_choices
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
 [X] 0003_alter_user_email_max_length
 [X] 0004_alter_user_username_opts
 [X] 0005_alter_user_last_login_null
 [X] 0006_require_contenttypes_0002
 [X] 0007_alter_validators_add_error_messages
 [X] 0008_alter_user_username_max_length
 [X] 0009_alter_user_last_name_max_length
contenttypes
 [X] 0001_initial
 [X] 0002_remove_content_type_name
historical_data
 [X] 0001_initial
 [X] 0002_auto_20181112_1950
sessions
 [X] 0001_initial

Cela répertorie toutes les applications du projet et les migrations associées à chaque application. De plus, cela mettra un gros X à côté des migrations déjà appliquées.

Pour notre petit exemple, le showmigrations La commande n'est pas particulièrement excitante, mais elle est pratique lorsque vous commencez à travailler sur une base de code existante ou que vous travaillez dans une équipe où vous n'êtes pas la seule personne à ajouter des migrations.



Désappliquer les migrations

Vous savez maintenant comment apporter des modifications au schéma de votre base de données en créant et en appliquant des migrations. À un moment donné, vous souhaiterez peut-être annuler les modifications et revenir à un schéma de base de données antérieur car vous :

  • Vous souhaitez tester une migration rédigée par un collègue
  • Réalisez qu'une modification que vous avez apportée était une mauvaise idée
  • Travailler sur plusieurs fonctionnalités avec différentes modifications de la base de données en parallèle
  • Vous souhaitez restaurer une sauvegarde qui a été créée alors que la base de données avait encore un ancien schéma

Heureusement, les migrations ne doivent pas nécessairement être à sens unique. Dans de nombreux cas, les effets d'une migration peuvent être annulés en annulant l'application d'une migration. Pour annuler une migration, vous devez appeler migrate avec le nom de l'application et le nom de la migration avant la migration que vous souhaitez annuler.

Si vous souhaitez annuler la migration 0002_auto_20181112_1950 dans vos historical_data app, vous devez passer 0001_initial comme argument du migrate commande :

$ python manage.py migrate historical_data 0001_initial
Operations to perform:
  Target specific migration: 0001_initial, from historical_data
Running migrations:
  Rendering model states... DONE
  Unapplying historical_data.0002_auto_20181112_1950... OK

La migration n'a pas été appliquée, ce qui signifie que les modifications apportées à la base de données ont été annulées.

Annuler l'application d'une migration ne supprime pas son fichier de migration. La prochaine fois que vous exécuterez la commande migrate commande, la migration sera à nouveau appliquée.

Attention : Ne confondez pas les migrations annulées avec l'opération d'annulation à laquelle vous êtes habitué depuis votre éditeur de texte préféré.

Toutes les opérations de base de données ne peuvent pas être complètement annulées. Si vous supprimez un champ d'un modèle, créez une migration et l'appliquez, Django supprimera la colonne correspondante de la base de données.

Si vous annulez l'application de cette migration, la colonne sera recréée, mais les données stockées dans cette colonne ne seront pas récupérées !

Lorsque vous traitez des noms de migration, Django vous évite quelques frappes en ne vous obligeant pas à épeler le nom complet de la migration. Il lui faut juste assez de nom pour l'identifier de manière unique.

Dans l'exemple précédent, il aurait suffi de lancer python manage.py migrate historical_data 0001 .



Dénommer les migrations

Dans l'exemple ci-dessus, Django a proposé un nom pour la migration basé sur l'horodatage, quelque chose comme *0002_auto_20181112_1950 . Si cela ne vous convient pas, vous pouvez utiliser le --name paramètre pour fournir un nom personnalisé (sans le .py extension).

Pour essayer cela, vous devez d'abord supprimer l'ancienne migration. Vous l'avez déjà désappliqué, vous pouvez donc supprimer le fichier en toute sécurité :

$ rm historical_data/migrations/0002_auto_20181112_1950.py

Vous pouvez maintenant le recréer avec un nom plus descriptif :

$ ./manage.py makemigrations historical_data --name switch_to_decimals

Cela créera la même migration qu'avant sauf avec le nouveau nom de 0002_switch_to_decimals .



Conclusion

Vous avez parcouru pas mal de terrain dans ce didacticiel et appris les bases des migrations Django.

Pour récapituler, les étapes de base pour utiliser les migrations Django ressemblent à ceci :

  1. Créer ou mettre à jour un modèle
  2. Exécutez ./manage.py makemigrations <app_name>
  3. Exécutez ./manage.py migrate pour tout migrer ou ./manage.py migrate <app_name> pour migrer une application individuelle
  4. Répéter si nécessaire

C'est ça! Ce flux de travail fonctionnera la plupart du temps, mais si les choses ne fonctionnent pas comme prévu, vous savez également comment répertorier et annuler les migrations.

Si vous avez précédemment créé et modifié vos tables de base de données avec du SQL écrit à la main, vous êtes maintenant devenu beaucoup plus efficace en déléguant ce travail aux migrations Django.

Dans le prochain didacticiel de cette série, vous approfondirez le sujet et découvrirez comment les migrations Django fonctionnent sous le capot.

Bonus gratuit : Cliquez ici pour accéder à un guide gratuit des ressources d'apprentissage Django (PDF) qui vous montre des trucs et astuces ainsi que les pièges courants à éviter lors de la création d'applications Web Python + Django.

Santé !



Vidéo