Usa più database in Django con una sola tabella "django_migrations"


11

Per un progetto in Django devo usare due database: predefinito e remoto . Ho creato routers.pye tutto funziona bene.

C'era la necessità di creare una tabella sul database remoto e ho creato la migrazione, eseguirlo e la tabella è django_migrationsstata creata. Voglio avere solo una tabella django_migrations, nel database predefinito.

La parte rilevante di routers.pyè qui:

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

Eseguo la migrazione in questo modo:

python manage.py migrate my_app --database=remote

Ora quando lo faccio:

python manage.py runserver

Ricevo 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: my_app.
Esegui 'python manage.py migrate' per applicarli.

Le tabelle per my_appvengono create nel remotedatabase e django_migrationsall'interno del remotedatabase le migrazioni sono contrassegnate come applicate.

EDIT:
Come forzare Django a usare solo una tabella django_migrations, ma applicare comunque le migrazioni in database diversi?

Come applicare le migrazioni in diversi database in modo che non vengano generati avvisi?


1
per altre app che non sono "my_app", allow_migrate restituisce None. Forse vuoi fare un altro controllo lì? Da quanto ho capito dal tuo router, "my_app" utilizza il database "remoto" e tutte le altre app utilizzeranno il database "predefinito"?
Martin Taleski,

@cezar Chiedete quasi impossibile. Per avere una django_migrationstabella condivisa , sarà necessario differenziare le righe con migrazioni per defaulte remotedb. Questo è abbastanza profondo negli interni del django. Vorrei anche rischiare di affermare che richiederebbe una grande riscrittura del codice di migrazione.
Kamil Niski,

@KamilNiski grazie per aver condiviso i tuoi pensieri. Rielaborerò la domanda.
Cezar,

Questo problema potrebbe essere rilevante.
Kevin Christopher Henry,

Risposte:


2

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_migrationsquando 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 fooe un'app barall'interno del progetto. L'app barha 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.pyregoliamo 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 barin INSTALLED_APPS( foo/settings.py) e crea le migrazioni:

python manage.py makemigrations bar

Prima di eseguire le migrazioni che creiamo routers.pyall'interno bardell'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 barnel remotedatabase:

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 remotedatabase:

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_bazvenga creata solo nel remotedatabase, ma Django segnerà le migrazioni come applicate in entrambi i database. Anche i tavoli per auth, admin, sessions, ecc saranno creati solo nella defaultbanca dati, come specificato routers.py. La tabella django_migrationsnel remotedatabase 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 .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.