Use múltiples bases de datos en Django con una sola tabla "django_migrations"

11

Para un proyecto en Django, tengo que usar dos bases de datos: predeterminada y remota . He creado routers.pyy todo funciona bien.

Había un requisito para crear una tabla en la base de datos remota y creé la migración, la ejecuté y la tabla django_migrations se creó . Quiero tener solo una tabla django_migrations, en la base de datos predeterminada.

La parte relevante de routers.pyestá aquí:

class MyRouter(object):
     # ...
     def allow_migrate(self, db, app_label, model_name=None, **hints):
         if app_label == 'my_app':
             return db == 'remote'
         return None

Ejecuto la migración así:

python manage.py migrate my_app --database=remote

Ahora cuando lo hago:

python manage.py runserver

Me sale la siguiente advertencia:

Tienes 1 migraciones no aplicadas. Es posible que su proyecto no funcione correctamente hasta que aplique las migraciones para las aplicaciones: my_app.
Ejecute 'python manage.py migrate' para aplicarlos.

Las tablas para my_appse crean en la remotebase de datos y django_migrationsdentro delremote base de datos, las migraciones se marcan como aplicadas.

EDITAR:
Cómo forzar a Django a usar solo una tabladjango_migrations , pero aún así aplicar las migraciones a diferentes bases de datos?

¿Cómo aplicar las migraciones en diferentes bases de datos para que no se generen advertencias?

cezar
fuente
1
para otras aplicaciones que no son 'my_app', allow_migrate devuelve None. ¿Tal vez quieres hacer otra verificación allí? Por lo que entiendo de su enrutador, 'my_app' usa la base de datos 'remota', y todas las demás aplicaciones usarán la base de datos 'predeterminada'.
Martin Taleski el
@cezar Pides casi imposible. Para tener una django_migrationstabla compartida , será necesario diferenciar entre filas con migraciones para defaulty remotedb. Esto es bastante profundo en las partes internas de django. Incluso me arriesgaría a afirmar que requeriría una gran reescritura del código de migración.
Kamil Niski el
@KamilNiski gracias por compartir tus pensamientos. Reformularé la pregunta.
cezar
Este problema puede ser relevante.
Kevin Christopher Henry

Respuestas:

2

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_migrationscuando 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 fooy una aplicación bardentro del proyecto. La aplicación barsolo 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.pyajustamos 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=defaultes 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 baren INSTALLED_APPS( foo/settings.py) y crearlas:

python manage.py makemigrations bar

Antes de ejecutar las migraciones que creamos routers.pydentro de la baraplicació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 baren la remotebase 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 remotebase 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_bazse cree solo en la remotebase 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 defaultbase de datos, como se especifica en routers.py. La tabla django_migrationsen la remotebase 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 .

cezar
fuente