Миграция Django была успешно применена, но база данных не была изменена
Мне нужно использовать дополнительную базу данных SQLite в новом проекте Django. Эта база данных находится в локальной файловой системе, но вне папки Django. Путь к нему указан в .env файле в корневом каталоге проекта Django.
Я хочу, чтобы Django мог управлять миграциями в этой базе данных, но у меня в ней уже есть данные, которые я не хочу потерять.
Мне удалось интегрировать базу данных в проект Django, и я не вижу никаких ошибок ни на одном этапе. Я могу извлекать данные из базы данных через оболочку Django. Однако, когда я пытаюсь применить миграцию, ничего не происходит: база данных не изменяется, но Django не выдает мне никакой ошибки (на самом деле это говорит о том, что миграция была применена).
Вот что я сделал:
- создал приложение-архиватор в Django
- в этом приложении создан файл
routers.pyсо следующим кодом:
class ArchiverDbRouter:
def db_for_read(self, model, **hints):
if model._meta.app_label in ['archiver']:
return 'archiver'
return None
def db_for_write(self, model, **hints):
if model._meta.app_label in ['archiver']:
return 'archiver'
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
if app_label in ['archiver']:
return db == 'archiver'
return None
- настроен
settings.pyна использование двух баз данных. Идея состоит в том, чтобы сохранить базу данных по умолчанию для всего Django, а затем базу данных "archiver" для приложения "archiver".
import os
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
USER_DIR = Path(os.getenv('USER_DIR', './user'))
(...)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
},
'archiver': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': USER_DIR / 'data/app.db',
}
}
DATABASE_ROUTERS = ['archiver.routers.ArchiverDbRouter']
- сгенерировал свои модели с помощью команды
inspectdb:
python manage.py inspectdb --database archiver > tempmodels.py
Затем отредактировал эти модели и сохранил их в archiver/models.py. Я удалил свойства managed = False во всех моделях.
- инициализировал базу данных
python manage.py migrate
При этом создается файл базы данных "по умолчанию".
- сгенерировал миграции для архиватора
python manage.py makemigrations archiver
Создан файл переноса 0001_initial.py.
- применил эту миграцию с флагом
--fake
python manage.py migrate archiver 0001 --fake
Я вижу соответствующую миграцию, сохраненную в таблице django_migrations.
На данный момент я могу использовать оболочку Django и получить доступ к фактическим данным в моей базе данных "архиватора", что, по-видимому, подтверждает, что маршрутизация работает правильно и база данных найдена Django. Например.
>>> q = State(account="test", name="test", value="test")
>>> q.save()
Затем я вижу, что новая строка (с тремя "тестовыми" значениями) присутствует в таблице "состояния" базы данных m "archiver" (с использованием стороннего инструмента HeidiSQL). Я также вижу, что дата изменения файла базы данных была обновлена.
внес некоторые изменения в мой
models.py, удалив поле, которое никогда не использовалось в моделиPost.снова сгенерировал миграции
python manage.py makemigrations archiver
Файл переноса создан:
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('archiver', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='post',
name='note',
),
]
- применен новый перенос
python manage.py migrate archiver
Это дает мне результат:
Operations to perform:
Apply all migrations: archiver
Running migrations:
Applying archiver.0002_remove_post_note... OK
Ошибки нет. Я вижу соответствующую миграцию, сохраненную в таблице django_migrations.
ОДНАКО, когда я просматриваю базу данных архиватора (снова используя HeidiSQL), поле "примечание" по-прежнему присутствует. Кроме того, "дата изменения" для файла базы данных не изменилась.
Что я упускаю?
Итак, при дальнейшем тестировании я заметил, что:
Запуск python manage.py sqlmigrate archiver 0002 --database=archiver показывает правильный результат SQL:
BEGIN;
--
-- Remove field note from post
--
ALTER TABLE "posts" DROP COLUMN "note";
COMMIT;
Принимая во внимание, что если я не использую флаг --database, то SQL-код в основном пуст:
$ python manage.py sqlmigrate archiver 0002
BEGIN;
--
-- Remove field note from post
--
-- (no-op)
COMMIT;
По-видимому, Django пытается применить миграцию к базе данных по умолчанию, что приводит к появлению фрагмента пустого SQL-кода, который, очевидно, не имеет никакого эффекта (но и не вызывает ошибки). Заметить это непросто.
Таким образом, похоже, что маршрутизатор не учитывается при использовании команды migrate, и решение состоит в том, чтобы просто указать его в целевой базе данных (если не используется база данных по умолчанию):
python manage.py migrate archiver --database=archiver