Благодаря комментариям на мой вопрос я провел небольшое исследование и сделал следующие выводы.
Использование нескольких баз данных приводит к созданию таблицы django_migrations
при использовании миграций. Там нет возможности записывать миграции только в одной таблице django_migrations
, как комментарий от Kamil Niski объясняет. Это понятно после прочтения файла django/db/migrations/recorder.py
.
Я проиллюстрирую пример с проектом foo
и приложением bar
внутри проекта. Приложение bar
имеет только одну модель Baz
.
Мы создаем проект:
django-admin startproject foo
Теперь у нас есть это содержимое в главном каталоге проекта:
- foo
- manage.py
У меня есть привычка группировать все приложения в каталоге проекта:
mkdir foo/bar
python manage.py bar foo/bar
В файле foo/settings.py
мы настраиваем параметры для использования двух разных баз данных, для целей этого примера мы используем 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'),
}
}
Теперь мы запускаем миграции:
python manage.py migrate --database=default
При этом выполняются все миграции, часть --database=default
является необязательной, поскольку, если не указано, Django использует базу данных по умолчанию.
Выполняемые операции: применить все миграции: admin, auth, типы содержимого, сеансы.
Выполнение миграции:
Применение contenttypes.0001_initial ... ОК
Применяем auth.0001_initial ... ОК
Применяем admin.0001_initial ... ОК
Применение admin.0002_logentry_remove_auto_add ... ОК
Применение admin.0003_logentry_add_action_flag_choices ... ОК
Применение contenttypes.0002_remove_content_type_name ... OK
Применение auth.0002_alter_permission_name_max_length ... OK
Применение auth.0003_alter_user_email_max_length ... OK
Применение auth.0004_alter_user_username_opts ... ОК
Применение auth.0005_alter_user_last_login_null ... ОК
Применение auth.0006_require_contenttypes_0002 ... ОК
Применение auth.0007_alter_validators_add_error_messages ... ОК
Применение auth.0008_alter_user_username_max_length ... OK
Применение auth.0009_alter_user_last_name_max_length ... OK
Применение auth.0010_alter_group_name_max_length ... OK
Применение auth.0011_update_proxy_permissions ... OK
Применение сеансов.0001_initial ... ОК
Django применил все миграции к базе данных по умолчанию:
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 сессий 0001_ininial 2019-11-13 16: 51: 05.063868
Теперь мы создаем модель Baz
:
models.py
:
from django.db import models
class Baz(models.Model):
name = models.CharField(max_length=255, unique=True)
зарегистрируйте приложение bar
в INSTALLED_APPS
( foo/settings.py
) и создайте миграции:
python manage.py makemigrations bar
Перед запуском миграций мы создаем routers.py
внутри bar
приложения:
Класс BarRouter (объект):
def db_for_read (self, model, ** hints):
if model._meta.app_label == 'bar':
вернуть «удаленный»
возврат Нет
def db_for_write (self, model, ** hints):
if model._meta.app_label == 'bar':
вернуть «удаленный»
возврат Нет
def allow_relation (self, obj1, obj2, ** hints):
возврат Нет
def allow_migrate (self, db, app_label, имя_модели = нет, ** подсказки):
if app_label == 'bar':
return db == 'remote'
если db == 'remote':
вернуть Ложь
возврат Нет
и зарегистрируйте его в foo/settings.py
:
DATABASE_ROUTERS = ['foo.bar.routers.BarRouter']
Теперь наивным подходом было бы запустить миграцию bar
в remote
базу данных:
python manage.py migrate bar --database=remote
Выполняемые операции: применить все миграции: bar
Запуск миграций:
Применение bar.0001_initial ... ОК
Миграции были применены к remote
базе данных:
1 бар 0001_initial 2019-11-13 17: 32: 39.701784
Когда мы бежим:
python manage.py runserver
будет выдано следующее предупреждение:
У вас есть 1 не примененная миграция. Ваш проект может не работать должным образом, пока вы не примените миграции для app (s): bar.
Запустите «python manage.py migrate», чтобы применить их.
Кажется, все работает нормально. Однако это предупреждение не удовлетворяет.
Правильный способ - запустить все миграции для каждой базы данных, как указано в этом ответе .
Это будет выглядеть так:
python manage.py migrate --database=default
python manage.py migrate --database=remote
и после создания миграций для bar
:
python manage.py migrate bar --database=default
python manage.py migrate bar --database=remote
Маршрутизатор позаботится о том, чтобы таблица bar_baz
создавалась только в remote
базе данных, но Django помечает миграции как применяемые в обеих базах данных. Также столы для auth
, admin
, sessions
и т.д. , будут созданы только в default
базе данных, как указано в routers.py
. Таблица django_migrations
в remote
базе данных также будет содержать записи для этих миграций.
Это долгое чтение, но я надеюсь, что оно проливает некоторый свет на эту, по моему мнению, не полностью объясненную проблему в официальной документации .