Как переместить модель между двумя приложениями 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 шага:

  1. Прежде чем что-либо делать, измените все свои ForeignKey ссылка на TheModel в Integerfield, Тогда беги python manage.py makemigrations
  2. Выполнив шаги Озана, переконвертируйте ваши внешние ключи: положите обратно ForeignKey(TheModel)вместо IntegerField(), Тогда сделайте миграцию снова (python manage.py makemigrations). Вы можете затем мигрировать, и это должно работать (python manage.py migrate)

Надеюсь, поможет. Конечно, протестируйте его на местном уровне, прежде чем пытаться в производстве, чтобы избежать плохих сюрпризов:)

Я получаю нервную миграцию ручного кодирования (как того требует ответ Озана), поэтому следующее объединяет стратегии Озана и Майкла для минимизации требуемого количества ручного кодирования:

  1. Перед перемещением любой модели убедитесь, что вы работаете с чистой базовой линией, запустив makemigrations,
  2. Переместить код для модели из app1 в app2
  3. В соответствии с рекомендациями @Michael, мы указываем новую модель на старую таблицу базы данных, используя db_table Мета-вариант на "новую" модель:

    class Meta:
        db_table = 'app1_yourmodel'
    
  4. Бежать makemigrations, Это сгенерирует CreateModel в app2 а также DeleteModel в app1, Технически, эти миграции ссылаются на одну и ту же таблицу и удаляют (включая все данные) и воссоздают таблицу.

  5. На самом деле, мы не хотим (или не нуждаемся) что-либо делать со столом. Нам просто нужно, чтобы Джанго верил, что изменение было сделано. За @ ответ Озана, state_operations флаг в SeparateDatabaseAndState Является ли это. Таким образом, мы завернем все migrations записи в обоих файлах МИГРАЦИИ с SeparateDatabaseAndState(state_operations=[...]), Например,

    operations = [
        ...
        migrations.DeleteModel(
            name='YourModel',
        ),
        ...
    ]
    

    становится

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=[
            ...
            migrations.DeleteModel(
                name='YourModel',
            ),
            ...
        ])
    ]
    
  6. РЕДАКТИРОВАТЬ: Вы также должны убедиться, что новый "виртуальный" 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 миграции:

  1. Скопировать модель из app1 в app2, задавать db_table, но НЕ меняйте ссылки на FK.
  2. Бежать makemigrations и завернуть все app2 миграция в state_operations (см. выше)
    • Как указано выше, добавьте зависимость в app2CreateTable до последней app1 миграция
  3. Укажите все ссылки FK на новую модель. Если вы не используете строковые ссылки, переместите старую модель в конец models.py (НЕ удаляйте его), чтобы он не конкурировал с импортированным классом.
  4. Бежать 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>'),
          ]  
      
  5. Удалить модели из app1

  6. Бежать makemigrations и обернуть app1 миграция в state_operations,
    • Добавить зависимость для всех ForeignKey миграции (то есть AlterField) из предыдущего шага (может включать миграции в app1 а также app2).
    • Когда я построил эти миграции, DeleteTable уже зависело от AlterField миграции, поэтому мне не нужно было применять его вручную (т.е. Alter до Delete).

На данный момент Джанго готов к работе. Новая модель указывает на старую таблицу, и миграции Django убедили ее, что все было перемещено надлежащим образом. Большая оговорка (из ответа @Michael) заключается в том, что новый ContentType создан для новой модели. Если вы ссылаетесь (например, ForeignKey) к типам контента, вам необходимо создать миграцию для обновления ContentType Таблица.

Я хотел очистить себя (мета-опции и имена таблиц), поэтому я использовал следующую процедуру (из @Michael):

  1. Удалить db_table Мета-запись
  2. Бежать makemigrations снова сгенерировать базу данных переименовать
  3. Отредактируйте эту последнюю миграцию и убедитесь, что она зависит от DeleteTable миграция. Не похоже, что это необходимо, поскольку Delete должно быть чисто логичным, но я столкнулся с ошибками (например, app1_yourmodel не существует) если я не

Как я это сделал (проверено на Django==1.8, с postgres, так что, вероятно, также 1.7)

ситуация

app1.YourModel

но вы хотите, чтобы он пошел:app2.YourModel

  1. Скопируйте YourModel (код) из app1 в app2.
  2. добавьте это в app2.YourModel:

    Class Meta:
        db_table = 'app1_yourmodel'
    
  3. $ python manage.py makemigrations app2

  4. Новая миграция (например, 0009_auto_something.py) выполняется в app2 с помощью оператора migrations.CreateModel(), переместите этот оператор в начальную миграцию app2 (например, 0001_initial.py) (это будет так же, как всегда). А теперь удалите созданную миграцию = 0009_auto_something.py

  5. Так же, как вы действуете, как app2.YourModel всегда был там, теперь удалите существование app1.YourModel из ваших миграций. Значение: закомментируйте операторы CreateModel и каждую настройку или миграцию данных, которые вы использовали после этого.

  6. И, конечно же, все ссылки на app1.YourModel должны быть изменены на app2.YourModel через ваш проект. Кроме того, не забывайте, что все возможные внешние ключи для app1.YourModel в миграциях должны быть изменены на app2.YourModel

  7. Теперь, если вы выполняете миграцию $ python manage.py, ничего не изменилось, также, когда вы выполняете make-миграции $ python manage.py, ничего нового не было обнаружено.

  8. Теперь последний штрих: удалите мета-класс из app2.YourModel и выполните $ python manage.py makemigrations app2 && python manage.py перенести app2 (если вы посмотрите на эту миграцию, вы увидите что-то вроде этого:)

        migrations.AlterModelTable(
        name='yourmodel',
        table=None,
    ),
    

table = None, означает, что он будет принимать имя таблицы по умолчанию, которое в этом случае будет app2_yourmodel.

  1. Совершено, с сохранением данных.

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))
            ],
        ),
    ]

Вы можете попробовать следующее (не проверено):

  1. переместить модель из src_app в dest_app
  2. мигрировать dest_app; убедитесь, что миграция схемы зависит от последних src_app миграция ( https://docs.djangoproject.com/en/dev/topics/migrations/)
  3. добавить миграцию данных в dest_app, который копирует все данные из src_app
  4. мигрировать src_app; убедитесь, что миграция схемы зависит от последней миграции (данных) dest_app - то есть: миграция шага 3

Обратите внимание, что вы будете копировать всю таблицу, а не перемещать ее, но в этом случае оба приложения не должны касаться таблицы, принадлежащей другому приложению, что, я думаю, является более важным.

  1. измените имена старых моделей на 'имя_модели_old'
  2. makemigrations
  3. создайте новые модели с именем 'model_name_new' с идентичными отношениями в связанных моделях (например, пользовательская модель теперь имеет user.blog_old и user.blog_new)
  4. makemigrations
  5. написать пользовательскую миграцию, которая переносит все данные в новые таблицы моделей
  6. протестируйте эти миграции, сравнивая резервные копии с новыми копиями базы данных до и после выполнения миграций
  7. когда все удовлетворительно, удалите старые модели
  8. makemigrations
  9. измените новые модели на правильное имя "model_name_new" -> "model_name"
  10. протестировать весь набор миграций на промежуточном сервере
  11. остановите ваш производственный сайт на несколько минут, чтобы запустить все миграции без вмешательства пользователей

Делайте это индивидуально для каждой модели, которую нужно переместить. Я бы не советовал делать то, что говорит другой ответ, переходя на целые числа и обратно на внешние ключи. Существует вероятность того, что новые внешние ключи будут другими, и строки могут иметь разные идентификаторы после миграции, и я не хотел рисковать несовпадения идентификаторов при переключении обратно на внешние ключи.

Допустим, вы перемещаете модель 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

Это все!

Другие вопросы по тегам