Grâce aux commentaires sur ma question, j'ai fait quelques recherches et suis arrivé aux conclusions suivantes.
L'utilisation de plusieurs bases de données entraîne la création d'une table django_migrations
lorsque des migrations sont utilisées. Il n'y a pas d'option pour enregistrer les migrations dans une seule table django_migrations
, comme l' explique le commentaire de Kamil Niski . C'est clair après avoir lu le fichier django/db/migrations/recorder.py
.
Je vais illustrer un exemple avec un projet foo
et une application bar
à l'intérieur du projet. L'application bar
n'a qu'un seul modèle Baz
.
Nous créons le projet:
django-admin startproject foo
Nous avons maintenant ces contenus dans le répertoire principal du projet:
- foo
- manage.py
J'ai l'habitude de regrouper toutes les applications dans le répertoire du projet:
mkdir foo/bar
python manage.py bar foo/bar
Dans le fichier, foo/settings.py
nous ajustons les paramètres pour utiliser deux bases de données différentes, aux fins de cet exemple, nous utilisons sqlite3
:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db1.sqlite3'),
},
'remote': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db2.sqlite3'),
}
}
Maintenant, nous exécutons les migrations:
python manage.py migrate --database=default
Cela exécute toutes les migrations, la partie --database=default
est facultative, car si non spécifié, Django utilise la base de données par défaut.
Opérations à effectuer:
Appliquer toutes les migrations: admin, auth, contenttypes, sessions
Exécution des migrations:
Application de contenttypes.0001_initial ... OK
Appliquer auth.0001_initial ... OK
Application de admin.0001_initial ... OK
Application de admin.0002_logentry_remove_auto_add ... OK
Application de admin.0003_logentry_add_action_flag_choices ... OK
Application de contenttypes.0002_remove_content_type_name ... OK
Appliquer auth.0002_alter_permission_name_max_length ... OK
Appliquer auth.0003_alter_user_email_max_length ... OK
Appliquer auth.0004_alter_user_username_opts ... OK
Appliquer auth.0005_alter_user_last_login_null ... OK
Appliquer auth.0006_require_contenttypes_0002 ... OK
Appliquer auth.0007_alter_validators_add_error_messages ... OK
Appliquer auth.0008_alter_user_username_max_length ... OK
Appliquer auth.0009_alter_user_last_name_max_length ... OK
Appliquer auth.0010_alter_group_name_max_length ... OK
Application d'auth.0011_update_proxy_permissions ... OK
Application de sessions.0001_initial ... OK
Django a appliqué toutes les migrations à la base de données par défaut:
1 contenttypes 0001_initial 2019-11-13 16: 51: 04.767382
2 auth 0001_initial 2019-11-13 16: 51: 04.792245
3 admin 0001_initial 2019-11-13 16: 51: 04.827454
4 admin 0002_logentr 2019-11-13 16: 51: 04.846627
5 admin 0003_logentr 2019-11-13 16: 51: 04.864458
6 contenttypes 0002_remove_ 2019-11-13 16: 51: 04.892220
7 auth 0002_alter_p 2019-11-13 16: 51: 04.906449
8 auth 0003_alter_u 2019-11-13 16: 51: 04.923902
9 auth 0004_alter_u 2019-11-13 16: 51: 04.941707
10 auth 0005_alter_u 2019-11-13 16: 51: 04.958371
11 auth 0006_require 2019-11-13 16: 51: 04.965527
12 auth 0007_alter_v 2019-11-13 16: 51: 04.981532
13 auth 0008_alter_u 2019-11-13 16: 51: 05.004149
14 auth 0009_alter_u 2019-11-13 16: 51: 05.019705
15 auth 0010_alter_g 2019-11-13 16: 51: 05.037023
16 auth 0011_update_ 2019-11-13 16: 51: 05.054449
17 sessions 0001_initial 2019-11-13 16: 51: 05.063868
Maintenant, nous créons le modèle Baz
:
models.py
:
from django.db import models
class Baz(models.Model):
name = models.CharField(max_length=255, unique=True)
enregistrer l'application bar
dans INSTALLED_APPS
( foo/settings.py
) et créer des migrations:
python manage.py makemigrations bar
Avant d'exécuter les migrations que nous créons routers.py
dans l' bar
application:
classe BarRouter (objet):
def db_for_read (auto, modèle, ** astuces):
si model._meta.app_label == 'bar':
retourner «à distance»
retour Aucun
def db_for_write (auto, modèle, ** astuces):
si model._meta.app_label == 'bar':
retourner «à distance»
retour Aucun
def allow_relation (self, obj1, obj2, ** hints):
retour Aucun
def allow_migrate (self, db, app_label, model_name = None, ** hints):
si app_label == 'bar':
return db == 'remote'
si db == 'distant':
retour Faux
retour Aucun
et enregistrez-le dans foo/settings.py
:
DATABASE_ROUTERS = ['foo.bar.routers.BarRouter']
Maintenant, l'approche naïve serait d'exécuter les migrations bar
dans la remote
base de données:
python manage.py migrate bar --database=remote
Opérations à effectuer:
Appliquer toutes les migrations: barre
Exécution des migrations:
Application de la barre 0001_initial ... OK
Les migrations ont été appliquées à la remote
base de données:
1 barre 0001_initial 2019-11-13 17: 32: 39.701784
Quand nous courons:
python manage.py runserver
l'avertissement suivant sera émis:
Vous avez 1 migration (s) non appliquée (s). Votre projet peut ne pas fonctionner correctement tant que vous n'avez pas appliqué les migrations pour les applications: bar.
Exécutez 'python manage.py migrate' pour les appliquer.
Cependant, tout semble bien fonctionner. Cependant, il n'est pas satisfaisant d'avoir cet avertissement.
La bonne façon serait d'exécuter toutes les migrations pour chaque base de données comme suggéré dans cette réponse .
Cela ressemblerait à ceci:
python manage.py migrate --database=default
python manage.py migrate --database=remote
et après avoir créé les migrations pour bar
:
python manage.py migrate bar --database=default
python manage.py migrate bar --database=remote
Le routeur veillera à ce que la table bar_baz
soit créée uniquement dans la remote
base de données, mais Django marquera les migrations comme appliquées dans les deux bases de données. Aussi les tables pour auth
, admin
, sessions
, etc. seront créés uniquement dans la default
base de données, comme indiqué dans routers.py
. La table django_migrations
de la remote
base de données contiendra également des enregistrements pour ces migrations.
C'est une longue lecture, mais j'espère que cela jette un peu de lumière sur ce problème, à mon avis, pas complètement expliqué dans la documentation officielle .