Как переместить модель между двумя приложениями Django (Django 1.7)
Примерно год назад я запустил проект, и, как и все новые разработчики, я не особо задумывался о структуре, однако теперь я, как и Джанго, начал понимать, что макет моего проекта, в основном мои модели, ужасен по своей структуре.,
У меня есть модели, которые в основном хранятся в одном приложении, и на самом деле большинство из этих моделей должны быть в своих отдельных приложениях, я попытался решить эту проблему и переместить их на юг, однако я нашел это сложным и действительно сложным из-за внешних ключей и т. Д.
Однако, благодаря Django 1.7 и встроенной поддержке миграции, есть ли лучший способ сделать это сейчас?
12 ответов
Я удаляю старый ответ, так как это может привести к потере данных. Как упоминал Озан, мы можем создать 2 миграции по одному в каждом приложении.
Первая миграция для удаления модели из первого приложения.
$ python manage.py makemigrations old_app --empty
Отредактируйте файл миграции, чтобы включить эти операции.
class Migration(migrations.Migration):
database_operations = [migrations.AlterModelTable('TheModel', 'newapp_themodel')]
state_operations = [migrations.DeleteModel('TheModel')]
operations = [
migrations.SeparateDatabaseAndState(
database_operations=database_operations,
state_operations=state_operations)
]
Вторая миграция, которая зависит от первой миграции, и создайте новую таблицу во втором приложении. После перемещения кода модели во второе приложение
$ python manage.py makemigrations new_app
и отредактируйте файл миграции примерно так.
class Migration(migrations.Migration):
dependencies = [
('old_app', 'above_migration')
]
state_operations = [
migrations.CreateModel(
name='TheModel',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
],
options={
'db_table': 'newapp_themodel',
},
bases=(models.Model,),
)
]
operations = [
migrations.SeparateDatabaseAndState(state_operations=state_operations)
]
Это можно сделать довольно легко, используя migrations.SeparateDatabaseAndState
, По сути, мы используем операцию базы данных, чтобы переименовать таблицу одновременно с двумя операциями состояния, чтобы удалить модель из истории одного приложения и создать ее в другой.
Удалить из старого приложения
python manage.py makemigrations old_app --empty
В миграции:
class Migration(migrations.Migration):
dependencies = []
database_operations = [
migrations.AlterModelTable('TheModel', 'newapp_themodel')
]
state_operations = [
migrations.DeleteModel('TheModel')
]
operations = [
migrations.SeparateDatabaseAndState(
database_operations=database_operations,
state_operations=state_operations)
]
Добавить в новое приложение
Сначала скопируйте модель в model.py нового приложения, затем:
python manage.py makemigrations new_app
Это создаст миграцию с наивным CreateModel
операция как единственная операция. Оберните это в SeparateDatabaseAndState
операция такая, что мы не пытаемся воссоздать таблицу. Также включите предыдущую миграцию в качестве зависимости:
class Migration(migrations.Migration):
dependencies = [
('old_app', 'above_migration')
]
state_operations = [
migrations.CreateModel(
name='TheModel',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
],
options={
'db_table': 'newapp_themodel',
},
bases=(models.Model,),
)
]
operations = [
migrations.SeparateDatabaseAndState(state_operations=state_operations)
]
Я столкнулся с той же проблемой. Ответ Озана мне очень помог, но, к сожалению, этого было недостаточно. На самом деле у меня было несколько ссылок ForeignKey на модель, которую я хотел переместить. После некоторой головной боли я нашел решение, поэтому решил опубликовать его, чтобы решить время людей.
Вам нужно еще 2 шага:
- Прежде чем что-либо делать, измените все свои
ForeignKey
ссылка наTheModel
вIntegerfield
, Тогда бегиpython manage.py makemigrations
- Выполнив шаги Озана, переконвертируйте ваши внешние ключи: положите обратно
ForeignKey(TheModel)
вместоIntegerField()
, Тогда сделайте миграцию снова (python manage.py makemigrations
). Вы можете затем мигрировать, и это должно работать (python manage.py migrate
)
Надеюсь, поможет. Конечно, протестируйте его на местном уровне, прежде чем пытаться в производстве, чтобы избежать плохих сюрпризов:)
Я получаю нервную миграцию ручного кодирования (как того требует ответ Озана), поэтому следующее объединяет стратегии Озана и Майкла для минимизации требуемого количества ручного кодирования:
- Перед перемещением любой модели убедитесь, что вы работаете с чистой базовой линией, запустив
makemigrations
, - Переместить код для модели из
app1
вapp2
В соответствии с рекомендациями @Michael, мы указываем новую модель на старую таблицу базы данных, используя
db_table
Мета-вариант на "новую" модель:class Meta: db_table = 'app1_yourmodel'
Бежать
makemigrations
, Это сгенерируетCreateModel
вapp2
а такжеDeleteModel
вapp1
, Технически, эти миграции ссылаются на одну и ту же таблицу и удаляют (включая все данные) и воссоздают таблицу.На самом деле, мы не хотим (или не нуждаемся) что-либо делать со столом. Нам просто нужно, чтобы Джанго верил, что изменение было сделано. За @ ответ Озана,
state_operations
флаг вSeparateDatabaseAndState
Является ли это. Таким образом, мы завернем всеmigrations
записи в обоих файлах МИГРАЦИИ сSeparateDatabaseAndState(state_operations=[...])
, Например,operations = [ ... migrations.DeleteModel( name='YourModel', ), ... ]
становится
operations = [ migrations.SeparateDatabaseAndState(state_operations=[ ... migrations.DeleteModel( name='YourModel', ), ... ]) ]
РЕДАКТИРОВАТЬ: Вы также должны убедиться, что новый "виртуальный"
CreateModel
Миграция зависит от любой миграции, которая фактически создала или изменила исходную таблицу. Например, если ваши новые миграцииapp2.migrations.0004_auto_<date>
(дляCreate
) а такжеapp1.migrations.0007_auto_<date>
(дляDelete
) самое простое:- открыто
app1.migrations.0007_auto_<date>
и скопировать егоapp1
зависимость (например,('app1', '0006...'),
). Это "непосредственно предшествующая" миграция вapp1
и должен включать зависимости от всей реальной логики построения модели. - открыто
app2.migrations.0004_auto_<date>
и добавить зависимость, которую вы только что скопировали в егоdependencies
список.
- открыто
РЕДАКТИРОВАТЬ: Если у вас есть ForeignKey
Отношение к модели, которую вы перемещаете, может не сработать. Это происходит потому, что:
- Зависимости не создаются автоматически для
ForeignKey
изменения - Мы не хотим, чтобы обернуть
ForeignKey
изменения вstate_operations
поэтому мы должны убедиться, что они отделены от табличных операций.
"Минимальный" набор операций различается в зависимости от ситуации, но следующая процедура должна работать для большинства / всех ForeignKey
миграции:
- Скопировать модель из
app1
вapp2
, задаватьdb_table
, но НЕ меняйте ссылки на FK. - Бежать
makemigrations
и завернуть всеapp2
миграция вstate_operations
(см. выше)- Как указано выше, добавьте зависимость в
app2
CreateTable
до последнейapp1
миграция
- Как указано выше, добавьте зависимость в
- Укажите все ссылки FK на новую модель. Если вы не используете строковые ссылки, переместите старую модель в конец
models.py
(НЕ удаляйте его), чтобы он не конкурировал с импортированным классом. Бежать
makemigrations
но ничего не заверните вstate_operations
(изменения FK должны действительно произойти). Добавить зависимость во всехForeignKey
миграции (то естьAlterField
) кCreateTable
миграция вapp2
(вам понадобится этот список для следующего шага, так что следите за ними). Например:- Найдите миграцию, которая включает
CreateModel
напримерapp2.migrations.0002_auto_<date>
и скопируйте название этой миграции. Найдите все миграции, которые имеют ForeignKey для этой модели (например, с помощью поиска
app2.YourModel
найти такие миграции, как:class Migration(migrations.Migration): dependencies = [ ('otherapp', '0001_initial'), ] operations = [ migrations.AlterField( model_name='relatedmodel', name='fieldname', field=models.ForeignKey(... to='app2.YourModel'), ), ]
Добавить
CreateModel
миграция как зависимость:class Migration(migrations.Migration): dependencies = [ ('otherapp', '0001_initial'), ('app2', '0002_auto_<date>'), ]
- Найдите миграцию, которая включает
Удалить модели из
app1
- Бежать
makemigrations
и обернутьapp1
миграция вstate_operations
,- Добавить зависимость для всех
ForeignKey
миграции (то естьAlterField
) из предыдущего шага (может включать миграции вapp1
а такжеapp2
). - Когда я построил эти миграции,
DeleteTable
уже зависело отAlterField
миграции, поэтому мне не нужно было применять его вручную (т.е.Alter
доDelete
).
- Добавить зависимость для всех
На данный момент Джанго готов к работе. Новая модель указывает на старую таблицу, и миграции Django убедили ее, что все было перемещено надлежащим образом. Большая оговорка (из ответа @Michael) заключается в том, что новый ContentType
создан для новой модели. Если вы ссылаетесь (например, ForeignKey
) к типам контента, вам необходимо создать миграцию для обновления ContentType
Таблица.
Я хотел очистить себя (мета-опции и имена таблиц), поэтому я использовал следующую процедуру (из @Michael):
- Удалить
db_table
Мета-запись - Бежать
makemigrations
снова сгенерировать базу данных переименовать - Отредактируйте эту последнюю миграцию и убедитесь, что она зависит от
DeleteTable
миграция. Не похоже, что это необходимо, посколькуDelete
должно быть чисто логичным, но я столкнулся с ошибками (например,app1_yourmodel
не существует) если я не
Как я это сделал (проверено на Django==1.8, с postgres, так что, вероятно, также 1.7)
ситуация
app1.YourModel
но вы хотите, чтобы он пошел:app2.YourModel
- Скопируйте YourModel (код) из app1 в app2.
добавьте это в app2.YourModel:
Class Meta: db_table = 'app1_yourmodel'
$ python manage.py makemigrations app2
Новая миграция (например, 0009_auto_something.py) выполняется в app2 с помощью оператора migrations.CreateModel(), переместите этот оператор в начальную миграцию app2 (например, 0001_initial.py) (это будет так же, как всегда). А теперь удалите созданную миграцию = 0009_auto_something.py
Так же, как вы действуете, как app2.YourModel всегда был там, теперь удалите существование app1.YourModel из ваших миграций. Значение: закомментируйте операторы CreateModel и каждую настройку или миграцию данных, которые вы использовали после этого.
И, конечно же, все ссылки на app1.YourModel должны быть изменены на app2.YourModel через ваш проект. Кроме того, не забывайте, что все возможные внешние ключи для app1.YourModel в миграциях должны быть изменены на app2.YourModel
Теперь, если вы выполняете миграцию $ python manage.py, ничего не изменилось, также, когда вы выполняете make-миграции $ python manage.py, ничего нового не было обнаружено.
Теперь последний штрих: удалите мета-класс из app2.YourModel и выполните $ python manage.py makemigrations app2 && python manage.py перенести app2 (если вы посмотрите на эту миграцию, вы увидите что-то вроде этого:)
migrations.AlterModelTable( name='yourmodel', table=None, ),
table = None, означает, что он будет принимать имя таблицы по умолчанию, которое в этом случае будет app2_yourmodel.
- Совершено, с сохранением данных.
PS во время миграции он увидит, что content_type app1.yourmodel был удален и может быть удален. Вы можете сказать "да" на это, но только если вы не используете его. В случае, если вы сильно зависите от него, чтобы FK с этим типом контента были неповрежденными, пока не отвечайте да или нет, но зайдите в базу данных того времени вручную, удалите contentype app2.yourmodel и переименуйте contenttype app1.yourmodel для app2.yourmodel, а затем продолжайте, отвечая нет.
Еще одна хакерская альтернатива, если данные не велики или не слишком сложны, но все же важны в обслуживании, заключается в
- Получить данные фиксаций используя manage.py dumpdata
- Перейдите к моделированию изменений и миграций должным образом, не связывая изменения
- Глобально замените приборы из старой модели и названия приложений на новые
- Загрузка данных с помощью manage.py loaddata
Введение
Мне пришлось переместить модель из одного приложения в другое.
Пробовал разные методы, например:
- добавление класса Meta: db_table = 'app1_yourmodel',
- миграции.SeparateDatabaseAndState,
- переименование имени таблицы вручную,
- копирование данных в процессе миграции путем выполнения необработанных SQL-запросов с помощью RunSQL,
- и т. д.
Но после каждого случая я сталкивался с какой-то ошибкой.
Я опишу метод, который я использовал и который мне очень помог.
Было очень полезно попрактиковаться в этом в моей среде разработки, имея копии файлов базы данных sqlite3 и имея возможность визуально видеть содержимое базы данных, пока я это делал.
Но для тех, кто не имеет доступа к файлу базы данных sqlite3 или не может предварительно просмотреть его содержимое в графическом интерфейсе, как я мог бы в VScode или в sqlitebrowser, я напишу как можно более подробные инструкции ниже. Впоследствии они помогли мне выполнить те же команды на моем сервере PROD (у меня тоже не было графического интерфейса).
Примечание: вы можете игнорировать--settings=settings.development
везде, где вы это увидите, оно вам не понадобится.
Если вы видите такую команду:
python manage.py makemigrations base_app --settings=settings.development
Это означает, что вам нужно запустить команду следующим образом:
python manage.py makemigrations base_app
И измените «base_app» на имя вашего приложения.
Мой предпочтительный метод
Итак, что я сделаю, это:
- Переместите файл models.py в новое приложение.
- «makemigrations» только для нового приложения
- «перенести» изменения только для нового приложения
- Подготовьтесь к копированию данных
- Запустите несколько необработанных команд sql, чтобы скопировать данные в новое приложение.
- «makemigrations» старого приложения, чтобы удалить старые таблицы
- Окончательная проверка
Переместите файл models.py в новое приложение.
мое старое приложение = base_app мое новое приложение = веб-сайт_fixes_app
Переместите файл models.py из старого приложения в новое. В старом приложении не должно оставаться файла models.py.
«makemigrations» только для нового приложения
Убедитесь, что у вас есть копия вашей текущей базы данных!
makemigrations ТОЛЬКО ДЛЯ НОВОГО ПРИЛОЖЕНИЯ, он создаст файл миграции для файла новой модели. Вы можете видеть, что я указываю новое имя приложения в команде makemigrations ниже, поэтому makemigrations не будет происходить глобально, а только для выбранного приложения.
python manage.py makemigrations website_fixes_app --settings=settings.development
«перенести» изменения только для нового приложения
Будут созданы новые таблицы. Обратите внимание: я лишь еще раз указываю новое имя приложения.
python manage.py migrate website_fixes_app --settings=settings.development
Теперь у вас есть два набора таблиц. Старые таблицы и новые таблицы. В обеих таблицах строки одинаковы.
Теперь самое интересное. Скопируйте данные из одного в другой!
Подготовьтесь к копированию данных
Поскольку я использую базу данных sqlite3, мне нужен какой-то «драйвер» для подключения к БД и выполнения запросов. Если вы используете другую БД, возможно, вам придется использовать другой драйвер. Команды SQL также должны быть похожими.
примечание: или сделайте это в программе просмотра БД для приложения sqlite, если можете. Лучше ВИДЕТЬ фактические изменения и содержимое в базе данных.
sudo apt install sqlite3
sqlite3 your_db_filename.sqlite3
Убедитесь, что таблицы были созданы в результате миграции.
# open the db
sqlite3 your_db_filename.sqlite3
Распечатайте имена таблиц и обратите внимание, что существуют как старые, так и новые таблицы.
.tables
SELECT * FROM old_table_name;
Запустите несколько необработанных команд sql, чтобы скопировать данные в новое приложение.
Откройте базу данных еще раз, если вы уже выходили из нее.
sqlite3 your_db_filename.sqlite3
Запустите эти команды SQL. Настройте поля и имена таблиц в соответствии с именами ваших таблиц.
Пример:
INSERT INTO your_new_table_name (id, title)
SELECT id, title
FROM your_old_table_name;
# then to check:
SELECT * FROM your_new_table_name;
В моем случае мне пришлось выполнить эти 3 запроса:
INSERT INTO website_fixes_app_websitefix (id, title, description, date_created, status)
SELECT id, title, description, date_created, status
FROM base_app_websitefix;
# check:
SELECT * FROM website_fixes_app_websitefix;
INSERT INTO website_fixes_app_websitefix_tags (id, websitefix_id, websitefixtag_id)
SELECT id, websitefix_id, websitefixtag_id
FROM base_app_websitefix_tags;
check:
SELECT * FROM website_fixes_app_websitefix_tags;
INSERT INTO website_fixes_app_websitefixtag (id, name)
SELECT id, name
FROM base_app_websitefixtag;
check:
SELECT * FROM website_fixes_app_websitefixtag;
выйдите из sqlite3 с помощьюCTRL + D
.
«makemigrations» старого приложения, чтобы удалить старые таблицы
Если ваши новые таблицы содержат данные старых таблиц, мы можем удалить старые таблицы (хорошо, что у вас есть копия вашей базы данных, так что не беспокойтесь, мы всегда можем вернуться назад).
Выполните миграцию старого приложения, чтобы удалить старые таблицы из базы данных.
Вы можете видеть, что я больше не делаю глобальную миграцию, я просто сосредотачиваюсь на одном приложении — моем старом приложении (base_app).
python manage.py makemigrations base_app --settings=settings.development
python manage.py migrate base_app --settings=settings.development
Окончательная проверка
проверьте, были ли удалены старые таблицы:
sqlite3 your_db_filename.sqlite3
.tables
SELECT * FROM old_table_name;
Запустите сервер и проверьте, работает ли приложение нормально. Если вы настроили свое приложение для чтения из новых таблиц, то оно должно работать безупречно.
Теперь мы можем удалить папку миграции из старого приложения.
Теперь всякий раз, когда вам придется модифицировать модели в новом приложении, вы сможете сделать это без проблем. Никаких ошибок не произойдет.
Скопировано из моего ответа на /questions/33251933/peremeschenie-modelej-mezhdu-prilozheniyami-django-18-s-neobhodimyimi-ssyilkami-na-foreignkey/33251942#33251942
Если вам нужно переместить модель, и у вас больше нет доступа к приложению (или вам не нужен доступ), вы можете создать новую операцию и рассмотреть возможность создания новой модели, только если перенесенная модель не имеет существовать.
В этом примере я передаю "MyModel" из old_app в myapp.
class MigrateOrCreateTable(migrations.CreateModel):
def __init__(self, source_table, dst_table, *args, **kwargs):
super(MigrateOrCreateTable, self).__init__(*args, **kwargs)
self.source_table = source_table
self.dst_table = dst_table
def database_forwards(self, app_label, schema_editor, from_state, to_state):
table_exists = self.source_table in schema_editor.connection.introspection.table_names()
if table_exists:
with schema_editor.connection.cursor() as cursor:
cursor.execute("RENAME TABLE {} TO {};".format(self.source_table, self.dst_table))
else:
return super(MigrateOrCreateTable, self).database_forwards(app_label, schema_editor, from_state, to_state)
class Migration(migrations.Migration):
dependencies = [
('myapp', '0002_some_migration'),
]
operations = [
MigrateOrCreateTable(
source_table='old_app_mymodel',
dst_table='myapp_mymodel',
name='MyModel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=18))
],
),
]
Вы можете попробовать следующее (не проверено):
- переместить модель из
src_app
вdest_app
- мигрировать
dest_app
; убедитесь, что миграция схемы зависит от последнихsrc_app
миграция ( https://docs.djangoproject.com/en/dev/topics/migrations/) - добавить миграцию данных в
dest_app
, который копирует все данные изsrc_app
- мигрировать
src_app
; убедитесь, что миграция схемы зависит от последней миграции (данных)dest_app
- то есть: миграция шага 3
Обратите внимание, что вы будете копировать всю таблицу, а не перемещать ее, но в этом случае оба приложения не должны касаться таблицы, принадлежащей другому приложению, что, я думаю, является более важным.
- измените имена старых моделей на 'имя_модели_old'
- makemigrations
- создайте новые модели с именем 'model_name_new' с идентичными отношениями в связанных моделях (например, пользовательская модель теперь имеет user.blog_old и user.blog_new)
- makemigrations
- написать пользовательскую миграцию, которая переносит все данные в новые таблицы моделей
- протестируйте эти миграции, сравнивая резервные копии с новыми копиями базы данных до и после выполнения миграций
- когда все удовлетворительно, удалите старые модели
- makemigrations
- измените новые модели на правильное имя "model_name_new" -> "model_name"
- протестировать весь набор миграций на промежуточном сервере
- остановите ваш производственный сайт на несколько минут, чтобы запустить все миграции без вмешательства пользователей
Делайте это индивидуально для каждой модели, которую нужно переместить. Я бы не советовал делать то, что говорит другой ответ, переходя на целые числа и обратно на внешние ключи. Существует вероятность того, что новые внешние ключи будут другими, и строки могут иметь разные идентификаторы после миграции, и я не хотел рисковать несовпадения идентификаторов при переключении обратно на внешние ключи.
Допустим, вы перемещаете модель TheModel из app_a в app_b.
Альтернативное решение - изменить существующие миграции вручную. Идея состоит в том, что каждый раз, когда вы видите операцию, изменяющую TheModel в миграциях app_a, вы копируете эту операцию в конец начальной миграции app_b. И каждый раз, когда вы видите ссылку 'app_a.TheModel' в миграциях app_a, вы меняете ее на 'app_b.TheModel'.
Я просто сделал это для существующего проекта, где я хотел извлечь определенную модель в повторно используемое приложение. Процедура прошла гладко. Я думаю, что было бы гораздо сложнее, если бы были ссылки от app_b на app_a. Кроме того, у меня была определенная вручную Meta.db_table для моей модели, которая могла бы помочь.
Примечательно, что в итоге вы получите измененную историю миграции. Это не имеет значения, даже если у вас есть база данных с примененными исходными миграциями. Если как исходная, так и переписанная миграции заканчиваются одной и той же схемой базы данных, тогда такая перезапись должна быть в порядке.
Это проверено примерно, так что не забудьте сделать резервную копию вашей БД!!!
Например, есть два приложения: src_app
а также dst_app
хотим переместить модель MoveMe
от src_app
в dst_app
,
Создайте пустые миграции для обоих приложений:
python manage.py makemigrations --empty src_app
python manage.py makemigrations --empty dst_app
Предположим, что новые миграции XXX1_src_app_new
а также XXX1_dst_app_new
Предыдущие вершины миграции XXX0_src_app_old
а также XXX0_dst_app_old
,
Добавьте операцию, которая переименовывает таблицу для MoveMe
модели и переименовывает его app_label в ProjectState, чтобы XXX1_dst_app_new
, Не забудьте добавить зависимость от XXX0_src_app_old
миграция. Результирующий XXX1_dst_app_new
миграция это:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
# this operations is almost the same as RenameModel
# https://github.com/django/django/blob/1.7/django/db/migrations/operations/models.py#L104
class MoveModelFromOtherApp(migrations.operations.base.Operation):
def __init__(self, name, old_app_label):
self.name = name
self.old_app_label = old_app_label
def state_forwards(self, app_label, state):
# Get all of the related objects we need to repoint
apps = state.render(skip_cache=True)
model = apps.get_model(self.old_app_label, self.name)
related_objects = model._meta.get_all_related_objects()
related_m2m_objects = model._meta.get_all_related_many_to_many_objects()
# Rename the model
state.models[app_label, self.name.lower()] = state.models.pop(
(self.old_app_label, self.name.lower())
)
state.models[app_label, self.name.lower()].app_label = app_label
for model_state in state.models.values():
try:
i = model_state.bases.index("%s.%s" % (self.old_app_label, self.name.lower()))
model_state.bases = model_state.bases[:i] + ("%s.%s" % (app_label, self.name.lower()),) + model_state.bases[i+1:]
except ValueError:
pass
# Repoint the FKs and M2Ms pointing to us
for related_object in (related_objects + related_m2m_objects):
# Use the new related key for self referential related objects.
if related_object.model == model:
related_key = (app_label, self.name.lower())
else:
related_key = (
related_object.model._meta.app_label,
related_object.model._meta.object_name.lower(),
)
new_fields = []
for name, field in state.models[related_key].fields:
if name == related_object.field.name:
field = field.clone()
field.rel.to = "%s.%s" % (app_label, self.name)
new_fields.append((name, field))
state.models[related_key].fields = new_fields
def database_forwards(self, app_label, schema_editor, from_state, to_state):
old_apps = from_state.render()
new_apps = to_state.render()
old_model = old_apps.get_model(self.old_app_label, self.name)
new_model = new_apps.get_model(app_label, self.name)
if self.allowed_to_migrate(schema_editor.connection.alias, new_model):
# Move the main table
schema_editor.alter_db_table(
new_model,
old_model._meta.db_table,
new_model._meta.db_table,
)
# Alter the fields pointing to us
related_objects = old_model._meta.get_all_related_objects()
related_m2m_objects = old_model._meta.get_all_related_many_to_many_objects()
for related_object in (related_objects + related_m2m_objects):
if related_object.model == old_model:
model = new_model
related_key = (app_label, self.name.lower())
else:
model = related_object.model
related_key = (
related_object.model._meta.app_label,
related_object.model._meta.object_name.lower(),
)
to_field = new_apps.get_model(
*related_key
)._meta.get_field_by_name(related_object.field.name)[0]
schema_editor.alter_field(
model,
related_object.field,
to_field,
)
def database_backwards(self, app_label, schema_editor, from_state, to_state):
self.old_app_label, app_label = app_label, self.old_app_label
self.database_forwards(app_label, schema_editor, from_state, to_state)
app_label, self.old_app_label = self.old_app_label, app_label
def describe(self):
return "Move %s from %s" % (self.name, self.old_app_label)
class Migration(migrations.Migration):
dependencies = [
('dst_app', 'XXX0_dst_app_old'),
('src_app', 'XXX0_src_app_old'),
]
operations = [
MoveModelFromOtherApp('MoveMe', 'src_app'),
]
Добавить зависимость от XXX1_dst_app_new
в XXX1_src_app_new
, XXX1_src_app_new
это неопорная миграция, которая необходима, чтобы убедиться, что будущее src_app
Миграции будут выполнены после XXX1_dst_app_new
,
Переехать MoveMe
от src_app/models.py
в dst_app/models.py
, Затем запустите:
python manage.py migrate
Это все!