Verwenden Sie mehrere Datenbanken in Django mit nur einer Tabelle "django_migrations".

11

Für ein Projekt in Django muss ich zwei Datenbanken verwenden: Standard und Remote . Ich habe erstellt routers.pyund alles funktioniert gut.

Es war erforderlich, eine Tabelle in der entfernten Datenbank zu erstellen, und ich habe eine Migration erstellt, sie ausgeführt und die Tabelle django_migrationswurde erstellt. Ich möchte nur eine Tabelle django_migrationsin der Standarddatenbank haben.

Der relevante Teil von routers.pyist hier:

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

Ich führe die Migration folgendermaßen aus:

python manage.py migrate my_app --database=remote

Jetzt, wenn ich es tue:

python manage.py runserver

Ich erhalte folgende Warnung:

Sie haben 1 nicht angewendete Migration (en). Ihr Projekt funktioniert möglicherweise erst ordnungsgemäß, wenn Sie die Migrationen für App (s) anwenden: my_app.
Führen Sie 'python manage.py migrate' aus, um sie anzuwenden.

Die Tabellen für my_appwerden in der remoteDatenbank erstellt, und in django_migrationsder remoteDatenbank werden die Migrationen als angewendet markiert.

BEARBEITEN:
Wie kann man Django zwingen, nur eine Tabelle zu verwenden django_migrationsund trotzdem die Migrationen in verschiedene Datenbanken anzuwenden?

Wie wende ich die Migrationen in verschiedenen Datenbanken an, damit keine Warnungen ausgegeben werden?

Cezar
quelle
1
Für andere Apps, die nicht 'my_app' sind, gibt allow_migrate None zurück. Vielleicht möchten Sie dort noch einmal nachsehen? Soweit ich von Ihrem Router weiß, verwendet 'my_app' die 'Remote'-Datenbank, und alle anderen Apps verwenden die' Standard'-Datenbank?
Martin Taleski
@cezar Du fragst nach fast unmöglich. Um eine gemeinsam genutzte django_migrationsTabelle zu haben , muss zwischen Zeilen mit Migrationen für defaultund remotedb unterschieden werden. Dies ist ziemlich tief in den Django-Interna. Ich würde sogar das Risiko eingehen, zu behaupten, dass der Migrationscode grundlegend neu geschrieben werden müsste.
Kamil Niski
@KamilNiski danke, dass du deine Gedanken geteilt hast. Ich werde die Frage umformulieren.
Cezar
Dieses Problem kann relevant sein.
Kevin Christopher Henry

Antworten:

2

Dank der Kommentare zu meiner Frage habe ich einige Nachforschungen angestellt und die folgenden Ergebnisse erzielt.

Die Verwendung mehrerer Datenbanken führt zur Erstellung einer Tabelle, django_migrationswenn Migrationen verwendet werden. Es gibt keine Möglichkeit, die Migrationen in nur einer Tabelle aufzuzeichnen django_migrations, wie der Kommentar von Kamil Niski erklärt. Dies ist nach dem Lesen der Datei klar django/db/migrations/recorder.py.

Ich werde ein Beispiel mit einem Projekt foound einer App barinnerhalb des Projekts veranschaulichen . Die App barhat nur ein Modell Baz.

Wir erstellen das Projekt:

django-admin startproject foo

Jetzt haben wir diese Inhalte im Hauptprojektverzeichnis:

- foo
- manage.py

Ich habe die Angewohnheit, alle Apps im Projektverzeichnis zu gruppieren:

mkdir foo/bar
python manage.py bar foo/bar

In der Datei foo/settings.pypassen wir die Einstellungen an, um zwei verschiedene Datenbanken zu verwenden. Für die Zwecke dieses Beispiels verwenden wir 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'),
    }
}

Jetzt führen wir die Migrationen durch:

python manage.py migrate --database=default

Dadurch werden alle Migrationen ausgeführt. Der Teil --database=defaultist optional, da Django die Standarddatenbank verwendet, wenn nichts angegeben ist.


  Durchzuführende Vorgänge : Alle Migrationen anwenden: admin, auth, contenttypes, session
 Ausführen von Migrationen:
  Anwenden von Inhaltstypen.0001_initial ... OK
  Auth.0001_initial anwenden ... OK
  Anwenden von admin.0001_initial ... OK
  Anwenden von admin.0002_logentry_remove_auto_add ... OK
  Anwenden von admin.0003_logentry_add_action_flag_choices ... OK
  Anwenden von Inhaltstypen.0002_remove_content_type_name ... OK
  Anwenden von auth.0002_alter_permission_name_max_length ... OK
  Anwenden von auth.0003_alter_user_email_max_length ... OK
  Anwenden von auth.0004_alter_user_username_opts ... OK
  Anwenden von auth.0005_alter_user_last_login_null ... OK
  Anwenden von auth.0006_require_contenttypes_0002 ... OK
  Anwenden von auth.0007_alter_validators_add_error_messages ... OK
  Anwenden von auth.0008_alter_user_username_max_length ... OK
  Anwenden von auth.0009_alter_user_last_name_max_length ... OK
  Anwenden von auth.0010_alter_group_name_max_length ... OK
  Anwenden von auth.0011_update_proxy_permissions ... OK
  Sessions anwenden.0001_initial ... OK

Django hat alle Migrationen auf die Standarddatenbank angewendet:

1 Inhaltstypen 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 Inhaltstypen 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 Sitzungen 0001_initial 2019-11-13 16: 51: 05.063868

Jetzt erstellen wir das Modell Baz:

models.py::

from django.db import models

class Baz(models.Model):
    name = models.CharField(max_length=255, unique=True)

Registrieren Sie die App barin INSTALLED_APPS( foo/settings.py) und erstellen Sie die Migrationen:

python manage.py makemigrations bar

Bevor wir die Migrationen ausführen, die wir routers.pyin der barApp erstellen :

Klasse BarRouter (Objekt):
    def db_for_read (Selbst, Modell, ** Hinweise):
        if model._meta.app_label == 'bar':
            'Remote' zurückgeben
        Rückgabe Keine

    def db_for_write (Selbst, Modell, ** Hinweise):
        if model._meta.app_label == 'bar':
            'Remote' zurückgeben
        Rückgabe Keine

    def allow_relation (self, obj1, obj2, ** Hinweise):
        Rückgabe Keine

    def allow_migrate (self, db, app_label, model_name = Keine, ** Hinweise):
        if app_label == 'bar':
            return db == 'remote'
        if db == 'remote':
            falsch zurückgeben
        Rückgabe Keine

und registrieren Sie es in foo/settings.py:

DATABASE_ROUTERS = ['foo.bar.routers.BarRouter']

Nun wäre der naive Ansatz, die Migrationen für barin die remoteDatenbank auszuführen :

python manage.py migrate bar --database=remote

  Durchzuführende Vorgänge: Alle Migrationen anwenden: Leiste
 Ausführen von Migrationen:
  Anwenden von bar.0001_initial ... OK

Die Migrationen wurden auf die remoteDatenbank angewendet :

1 bar 0001_initial 2019-11-13 17: 32: 39.701784

Wenn wir rennen:

python manage.py runserver

Die folgende Warnung wird ausgegeben:

Sie haben 1 nicht angewendete Migration (en). Ihr Projekt funktioniert möglicherweise erst ordnungsgemäß, wenn Sie die Migrationen für App (s): bar anwenden.
Führen Sie 'python manage.py migrate' aus, um sie anzuwenden.

Alles scheint gut zu funktionieren. Es ist jedoch nicht zufriedenstellend, diese Warnung zu haben.

Der richtige Weg wäre, alle Migrationen für jede Datenbank auszuführen, wie in dieser Antwort vorgeschlagen .

Es würde so aussehen:

python manage.py migrate --database=default
python manage.py migrate --database=remote

und nach dem Erstellen der Migrationen für bar:

python manage.py migrate bar --database=default
python manage.py migrate bar --database=remote

Der Router sorgt dafür, dass die Tabelle bar_baznur in der remoteDatenbank erstellt wird, aber Django markiert die Migrationen als in beiden Datenbanken angewendet. Auch die Tabellen für auth, admin, sessionsetc. werden nur in der erstellt werden defaultDatenbank, wie angegeben in routers.py. Die Tabelle django_migrationsin der remoteDatenbank enthält auch Datensätze für diese Migrationen.

Es ist eine lange Lektüre, aber ich hoffe, sie wirft ein Licht auf dieses meiner Meinung nach nicht gründlich erläuterte Problem in der offiziellen Dokumentation .

Cezar
quelle