Grazie ai commenti sulla mia domanda ho fatto alcune ricerche e ho trovato i seguenti risultati.
L'uso di più database comporta la creazione di una tabella django_migrations
quando vengono utilizzate le migrazioni. Non è possibile registrare le migrazioni in una sola tabella django_migrations
, come spiega il commento di Kamil Niski . Questo è chiaro dopo aver letto il file django/db/migrations/recorder.py
.
Illustrerò un esempio con un progetto foo
e un'app bar
all'interno del progetto. L'app bar
ha un solo modello Baz
.
Creiamo il progetto:
django-admin startproject foo
Ora abbiamo questi contenuti nella directory principale del progetto:
- foo
- manage.py
Ho l'abitudine di raggruppare tutte le app all'interno della directory del progetto:
mkdir foo/bar
python manage.py bar foo/bar
Nel file foo/settings.py
regoliamo le impostazioni per utilizzare due diversi database, ai fini di questo esempio utilizziamo 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'),
}
}
Ora eseguiamo le migrazioni:
python manage.py migrate --database=default
Questo esegue tutte le migrazioni, la parte --database=default
è facoltativa, perché se non specificato Django utilizza il database predefinito.
Operazioni da eseguire:
applicare tutte le migrazioni: admin, auth, tipi di contenuto, sessioni
Esecuzione delle migrazioni:
Applicazione contenttypes.0001_initial ... OK
Applicazione auth.0001_initial ... OK
Applicazione admin.0001_initial ... OK
Applicazione admin.0002_logentry_remove_auto_add ... OK
Applicazione admin.0003_logentry_add_action_flag_choices ... OK
Applicazione contenttypes.0002_remove_content_type_name ... OK
Applicazione di auth.0002_alter_permission_name_max_length ... OK
Applicazione auth.0003_alter_user_email_max_length ... OK
Applicazione di auth.0004_alter_user_username_opts ... OK
Applicazione auth.0005_alter_user_last_login_null ... OK
Applicazione di auth.0006_require_contenttypes_0002 ... OK
Applicazione di auth.0007_alter_validators_add_error_messages ... OK
Applicazione auth.0008_alter_user_username_max_length ... OK
Applicazione auth.0009_alter_user_last_name_max_length ... OK
Applicando auth.0010_alter_group_name_max_length ... OK
Applicazione auth.0011_update_proxy_permissions ... OK
Applicazione delle sessioni.0001_iniziale ... OK
Django ha applicato tutte le migrazioni al database predefinito:
1 contenttypes 0001_initial 2019-11-13 16: 51: 04.767382
2 auth 0001_iniziale 2019-11-13 16: 51: 04.792245
3 admin 0001_iniziale 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 sessioni 0001_iniziale 2019-11-13 16: 51: 05.063868
Ora creiamo il modello Baz
:
models.py
:
from django.db import models
class Baz(models.Model):
name = models.CharField(max_length=255, unique=True)
registra l'app bar
in INSTALLED_APPS
( foo/settings.py
) e crea le migrazioni:
python manage.py makemigrations bar
Prima di eseguire le migrazioni che creiamo routers.py
all'interno bar
dell'app:
classe BarRouter (oggetto):
def db_for_read (sé, modello, ** suggerimenti):
if model._meta.app_label == 'bar':
ritorna "remoto"
restituisce Nessuno
def db_for_write (sé, modello, ** suggerimenti):
if model._meta.app_label == 'bar':
ritorna "remoto"
restituisce Nessuno
def allow_relation (self, obj1, obj2, ** suggerimenti):
restituisce Nessuno
def allow_migrate (self, db, app_label, model_name = None, ** suggerimenti):
se app_label == 'bar':
return db == 'remote'
se db == "remoto":
restituisce False
restituisce Nessuno
e registralo in foo/settings.py
:
DATABASE_ROUTERS = ['foo.bar.routers.BarRouter']
Ora l'approccio ingenuo sarebbe quello di eseguire le migrazioni bar
nel remote
database:
python manage.py migrate bar --database=remote
Operazioni da eseguire:
applicare tutte le migrazioni: barra
Esecuzione delle migrazioni:
Applicazione bar.0001_initial ... OK
Le migrazioni sono state applicate al remote
database:
1 bar 0001_iniziale 2019-11-13 17: 32: 39.701784
Quando corriamo:
python manage.py runserver
verrà generato il seguente avviso:
Hai 1 migrazione / i non applicata. Il tuo progetto potrebbe non funzionare correttamente fino a quando non applichi le migrazioni per le app: bar.
Esegui 'python manage.py migrate' per applicarli.
Tutto sembra funzionare bene però. Tuttavia non è soddisfacente avere questo avviso.
Il modo corretto sarebbe quello di eseguire tutte le migrazioni per ciascun database come suggerito in questa risposta .
Sarebbe così:
python manage.py migrate --database=default
python manage.py migrate --database=remote
e dopo aver creato le migrazioni per bar
:
python manage.py migrate bar --database=default
python manage.py migrate bar --database=remote
Il router farà in modo che la tabella bar_baz
venga creata solo nel remote
database, ma Django segnerà le migrazioni come applicate in entrambi i database. Anche i tavoli per auth
, admin
, sessions
, ecc saranno creati solo nella default
banca dati, come specificato routers.py
. La tabella django_migrations
nel remote
database avrà record anche per queste migrazioni.
È una lettura lunga, ma spero che faccia luce su questo argomento, secondo me, non completamente spiegato nella documentazione ufficiale .