Gunakan banyak basis data di Django dengan hanya satu tabel “django_migrations”

11

Untuk proyek di Django saya harus menggunakan dua database: default dan remote . Saya telah membuat routers.pydan semuanya berfungsi dengan baik.

Ada persyaratan untuk membuat tabel di basis data jauh dan saya membuat migrasi, jalankan, dan tabel django_migrationsitu dibuat. Saya ingin hanya memiliki satu tabel django_migrations, dalam database default.

Bagian yang relevan routers.pyada di sini:

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

Saya menjalankan migrasi seperti ini:

python manage.py migrate my_app --database=remote

Sekarang ketika saya melakukannya:

python manage.py runserver

Saya mendapatkan peringatan berikut:

Anda memiliki 1 migrasi yang tidak diterapkan. Proyek Anda mungkin tidak berfungsi dengan baik sampai Anda menerapkan migrasi untuk aplikasi: my_app.
Jalankan 'python manage.py migrate' untuk menerapkannya.

Tabel untuk my_appdibuat di remotedatabase, dan di django_migrationsdalam remotedatabase migrasi ditandai sebagai diterapkan.

EDIT:
Bagaimana cara memaksa Django untuk menggunakan hanya satu tabel django_migrations, tetapi masih menerapkan migrasi ke database yang berbeda?

Bagaimana cara menerapkan migrasi di berbagai basis data sehingga tidak ada peringatan yang dimunculkan?

cezar
sumber
1
untuk aplikasi lain yang bukan 'my_app', allow_migrate kembali Tidak ada. Mungkin Anda ingin melakukan pemeriksaan lagi di sana? Dari apa yang saya mengerti dari router Anda, 'my_app' menggunakan database 'remote', dan semua aplikasi lain akan menggunakan database 'default'?
Martin Taleski
@cezar Anda meminta hampir mustahil. Untuk memiliki django_migrationstabel bersama , akan diperlukan untuk membedakan antara baris dengan migrasi untuk defaultdan remotedb. Ini cukup dalam di internal Django. Saya bahkan akan mengambil risiko menyatakan bahwa itu akan memerlukan penulisan ulang kode migrasi.
Kamil Niski
@KamilNiski terima kasih telah berbagi pemikiran Anda. Saya akan menulis ulang pertanyaannya.
cezar
Masalah ini mungkin relevan.
Kevin Christopher Henry

Jawaban:

2

Berkat komentar pada pertanyaan saya, saya melakukan riset dan menghasilkan temuan berikut.

Menggunakan banyak basis data menghasilkan pembuatan tabel django_migrationssaat migrasi digunakan. Tidak ada pilihan untuk mencatat migrasi hanya dalam satu tabel django_migrations, seperti komentar dari Kamil Niski menjelaskan. Ini jelas setelah membaca file django/db/migrations/recorder.py.

Saya akan menggambarkan contoh dengan proyek foodan aplikasi bardi dalam proyek. Aplikasi barini hanya memiliki satu model Baz.

Kami membuat proyek:

django-admin startproject foo

Sekarang kita memiliki konten ini di dalam direktori proyek utama:

- foo
- manage.py

Saya memiliki kebiasaan untuk mengelompokkan semua aplikasi di dalam direktori proyek:

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

Dalam file tersebut foo/settings.pykami menyesuaikan pengaturan untuk menggunakan dua database yang berbeda, untuk keperluan contoh ini kami menggunakan 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'),
    }
}

Sekarang kami menjalankan migrasi:

python manage.py migrate --database=default

Ini menjalankan semua migrasi, bagian --database=defaultitu opsional, karena jika tidak ditentukan Django menggunakan database default.

Operasi yang harus dilakukan: 
  Terapkan semua migrasi: admin, auth, tipe konten, sesi
 Menjalankan migrasi:
  Menerapkan tipe konten.0001_initial ... OK
  Menerapkan auth,0001_initial ... OK
  Menerapkan admin.0001_initial ... OK
  Menerapkan admin.0002_logentry_remove_auto_add ... OK
  Menerapkan admin.0003_logentry_add_action_flag_choices ... OK
  Menerapkan contenttypes.0002_remove_content_type_name ... OK
  Menerapkan auth.0002_alter_permission_name_max_length ... OK
  Menerapkan auth,0003_alter_user_email_max_length ... OK
  Menerapkan auth,0004_alter_user_username_opts ... OK
  Menerapkan auth,0005_alter_user_last_login_null ... OK
  Menerapkan auth,0006_require_contenttypes_0002 ... OK
  Menerapkan auth.0007_alter_validators_add_error_messages ... OK
  Menerapkan auth,0008_alter_user_username_max_length ... OK
  Menerapkan auth,0009_alter_user_last_name_max_length ... OK
  Menerapkan auth.0010_alter_group_name_max_length ... OK
  Menerapkan auth.0011_update_proxy_permissions ... OK
  Menerapkan sesi.0001_initial ... OK

Django telah menerapkan semua migrasi ke database default:

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

Sekarang kami membuat model Baz:

models.py:

from django.db import models

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

daftarkan aplikasinya barke INSTALLED_APPS( foo/settings.py) dan buat themigrations:

python manage.py makemigrations bar

Sebelum kami menjalankan migrasi yang kami buat routers.pydi dalam baraplikasi:

kelas BarRouter (objek):
    def db_for_read (self, model, ** hints):
        jika model._meta.app_label == 'bar':
            kembalikan 'jarak jauh'
        return Tidak ada

    def db_for_write (self, model, ** hints):
        jika model._meta.app_label == 'bar':
            kembalikan 'jarak jauh'
        return Tidak ada

    def allow_relation (self, obj1, obj2, ** hints):
        return Tidak ada

    def allow_migrate (self, db, app_label, model_name = Tidak ada, ** petunjuk):
        jika app_label == 'bar':
            return db == 'remote'
        if db == 'remote':
            mengembalikan False
        return Tidak ada

dan daftarkan di foo/settings.py:

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

Sekarang pendekatan naif adalah menjalankan migrasi barke dalam remotedatabase:

python manage.py migrate bar --database=remote
Operasi yang harus dilakukan: 
  Terapkan semua migrasi: bilah
 Menjalankan migrasi:
  Menerapkan bar.0001_initial ... OK

Migrasi telah diterapkan ke remotedatabase:

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

Ketika kami menjalankan:

python manage.py runserver

peringatan berikut akan dimunculkan:

Anda memiliki 1 migrasi yang tidak diterapkan. Proyek Anda mungkin tidak berfungsi dengan baik sampai Anda menerapkan migrasi untuk aplikasi: bilah.
Jalankan 'python manage.py migrate' untuk menerapkannya.

Segalanya tampak bekerja dengan baik. Namun tidak memuaskan memiliki peringatan ini.

Cara yang tepat adalah menjalankan semua migrasi untuk setiap basis data seperti yang disarankan dalam jawaban ini .

Akan terlihat seperti ini:

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

dan setelah membuat migrasi untuk bar:

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

Router akan berhati-hati bahwa tabel bar_bazdibuat hanya dalam remotedatabase, tetapi Django akan menandai migrasi sebagaimana diterapkan di kedua database. Juga tabel untuk auth, admin, sessions, dll akan dibuat hanya dalam defaultbasis data, sebagaimana ditentukan dalam routers.py. Tabel django_migrationsdalam remotedatabase akan memiliki catatan untuk migrasi ini juga.

Ini adalah bacaan yang panjang, tapi saya harap ini menjelaskan hal ini, menurut pendapat saya, tidak menjelaskan masalah secara menyeluruh dalam dokumentasi resmi .

cezar
sumber