Gracias a los comentarios sobre mi pregunta, investigué un poco y obtuve los siguientes hallazgos.
El uso de múltiples bases de datos da como resultado la creación de una tabla django_migrations
cuando se utilizan migraciones. No hay opción para registrar las migraciones en una sola tabla django_migrations
, como explica el comentario de Kamil Niski . Esto queda claro después de leer el archivodjango/db/migrations/recorder.py
.
Ilustraré un ejemplo con un proyecto foo
y una aplicación bar
dentro del proyecto. La aplicación bar
solo tiene un modelo Baz
.
Creamos el proyecto:
django-admin startproject foo
Ahora tenemos estos contenidos dentro del directorio principal del proyecto:
- foo
- manage.py
Tengo la costumbre de agrupar todas las aplicaciones dentro del directorio del proyecto:
mkdir foo/bar
python manage.py bar foo/bar
En el archivo foo/settings.py
ajustamos la configuración para usar dos bases de datos diferentes, para los propósitos de este ejemplo usamos 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'),
}
}
Ahora ejecutamos las migraciones:
python manage.py migrate --database=default
Esto ejecuta todas las migraciones, la parte --database=default
es opcional, porque si no se especifica, Django usa la base de datos predeterminada.
Operaciones a realizar:
aplicar todas las migraciones: admin, autenticación, tipos de contenido, sesiones
Ejecutar migraciones:
Aplicando contenttypes.0001_initial ... OK
Aplicando auth.0001_initial ... OK
Aplicando admin.0001_initial ... OK
Aplicando admin.0002_logentry_remove_auto_add ... OK
Aplicando admin.0003_logentry_add_action_flag_choices ... OK
Aplicando contenttypes.0002_remove_content_type_name ... OK
Aplicando auth.0002_alter_permission_name_max_length ... OK
Aplicando auth.0003_alter_user_email_max_length ... OK
Aplicando auth.0004_alter_user_username_opts ... OK
Aplicando auth.0005_alter_user_last_login_null ... OK
Aplicando auth.0006_require_contenttypes_0002 ... OK
Aplicando auth.0007_alter_validators_add_error_messages ... OK
Aplicando auth.0008_alter_user_username_max_length ... OK
Aplicando auth.0009_alter_user_last_name_max_length ... OK
Aplicando auth.0010_alter_group_name_max_length ... OK
Aplicando auth.0011_update_proxy_permissions ... OK
Aplicando sesiones.0001_inicial ... OK
Django ha aplicado todas las migraciones a la base de datos predeterminada:
1 contenttypes 0001_initial 2019-11-13 16: 51: 04.767382
2 aut. 0001_inicial 2019-11-13 16: 51: 04.792245
3 admin 0001_inicial 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 tipos de contenido 0002_remove_ 2019-11-13 16: 51: 04.892220
7 autenticación 0002_alter_p 2019-11-13 16: 51: 04.906449
8 autenticación 0003_alter_u 2019-11-13 16: 51: 04.923902
9 autenticación 0004_alter_u 2019-11-13 16: 51: 04.941707
10 autenticación 0005_alter_u 2019-11-13 16: 51: 04.958371
11 aut. 0006_require 2019-11-13 16: 51: 04.965527
12 aut. 0007_alter_v 2019-11-13 16: 51: 04.981532
13 autenticación 0008_alter_u 2019-11-13 16: 51: 05.004149
14 autenticación 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 sesiones 0001_inicial 2019-11-13 16: 51: 05.063868
Ahora creamos el modelo Baz
:
models.py
:
from django.db import models
class Baz(models.Model):
name = models.CharField(max_length=255, unique=True)
registrar la aplicación bar
en INSTALLED_APPS
( foo/settings.py
) y crearlas:
python manage.py makemigrations bar
Antes de ejecutar las migraciones que creamos routers.py
dentro de la bar
aplicación:
clase BarRouter (objeto):
def db_for_read (self, model, ** pistas):
if model._meta.app_label == 'bar':
volver 'remoto'
regresar Ninguno
def db_for_write (self, model, ** pistas):
if model._meta.app_label == 'bar':
volver 'remoto'
regresar Ninguno
def allow_relation (self, obj1, obj2, ** sugerencias):
regresar Ninguno
def allow_migrate (self, db, app_label, model_name = None, ** sugerencias):
if app_label == 'bar':
return db == 'remoto'
si db == 'remoto':
falso retorno
regresar Ninguno
y regístralo en foo/settings.py
:
DATABASE_ROUTERS = ['foo.bar.routers.BarRouter']
Ahora, el enfoque ingenuo sería ejecutar las migraciones bar
en la remote
base de datos:
python manage.py migrate bar --database=remote
Operaciones a realizar:
Aplicar todas las migraciones: barra
Ejecutar migraciones:
Aplicando barra.0001_inicial ... OK
Las migraciones se han aplicado a la remote
base de datos:
1 bar 0001_inicial 2019-11-13 17: 32: 39.701784
Cuando corremos:
python manage.py runserver
se generará la siguiente advertencia:
Tienes 1 migraciones no aplicadas. Es posible que su proyecto no funcione correctamente hasta que aplique las migraciones para la (s) aplicación (es): barra.
Ejecute 'python manage.py migrate' para aplicarlos.
Sin embargo, todo parece funcionar bien. Sin embargo, no es satisfactorio tener esta advertencia.
La forma correcta sería ejecutar todas las migraciones para cada base de datos como se sugiere en esta respuesta .
Se vería así:
python manage.py migrate --database=default
python manage.py migrate --database=remote
y después de crear las migraciones para bar
:
python manage.py migrate bar --database=default
python manage.py migrate bar --database=remote
El enrutador se encargará de que la tabla bar_baz
se cree solo en la remote
base de datos, pero Django marcará las migraciones aplicadas en ambas bases de datos. También las mesas para auth
, admin
, sessions
, etc. serán creados sólo en la default
base de datos, como se especifica en routers.py
. La tabla django_migrations
en la remote
base de datos también tendrá registros para estas migraciones.
Es una lectura larga, pero espero que arroje algo de luz sobre esto, en mi opinión, no se explica a fondo el problema en la documentación oficial .