Как переместить модель между двумя приложениями Django (Django 1.7)

133

Итак, около года назад я начал проект и, как и все новые разработчики, на самом деле не уделял слишком много внимания структуре, однако теперь я продвинулся дальше вместе с Django, стало появляться, что мой макет проекта, в основном, мои модели ужасны по структуре ,

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

Однако из-за Django 1.7 и встроенной поддержки миграций есть ли лучший способ сделать это сейчас?

Сэм Бакингем
источник
4
Возможно, вы захотите изменить принятый ответ.
Бабкен Варданян
Для людей, которые столкнутся с этим в будущем: Django 3.x здесь, и подход, подробно описанный на realpython.com/move-django-model/… работал у меня. У меня было несколько внешних ключей между моделями в старом приложении и моделями в новом приложении.
pradeepcep

Ответы:

16

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

Первая миграция для удаления модели из 1-го приложения.

$ 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)
    ]
ChillarAnand
источник
У меня есть существующие данные, и многие из них я просто не могу потерять, возможно ли это сделать с этим?
Сэм Бэкингем
@KevinChristopherHenry Изменил код. Это сохраняет существующие данные.
ChillarAnand 06
@SamBuckingham Да, вы можете попробовать выполнить миграцию с измененным кодом без потери данных.
ChillarAnand
2
Я думаю, что это будет лучший способ, спасибо за всю помощь, ребята, это было великолепно.
Сэм Бэкингем
1
ИМО, это неправильное решение, основное предположение миграции заключается в том, что если вы запустите, ./manage.py migrateвсе закончится в хорошем состоянии. Подделка миграции вручную - это неправильный способ ИМО.
JB.
341

Это довольно легко сделать с помощью 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)
    ]
озан
источник
14
Действительно хорошее объяснение. Это должен быть ответ: переименовав таблицу, вы избежите потери данных.
Ремиз
11
Это лучший способ сделать это, и он намного лучше моего. Добавил примечание вверху моего ответа.
ChillarAnand
4
Я сделал это, но когда после этого я запускаю makemigrations в новом приложении, он генерирует миграцию AlterModelTable, переименовывая ее в None.
Diego Ponciano
4
Нашел способ решить свою проблему по этой инструкции. Проблема усложняется, если у вас есть ссылки на внешние ключи, которые являются обязательными полями. Мне пришлось добавить пару шагов, чтобы переместить ссылки.
Nostalg.io
14
По многочисленным просьбам я создал подробный ответ на миграцию модели FK на примере GitHub. stackoverflow.com/questions/30601107/…
Nostalg.io 03
26

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

Вам нужно еще 2 шага:

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

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

otranzer
источник
8
как насчет отношений ManyToManyField ??
Tomcounsell
1
@tomcounsell отличный комментарий, я бы предположил, добавив конкретную сквозную модель только для целей миграции. Чтобы данные остались нетронутыми, требуется много работы ...
Втауэр,
Поскольку отношение «многие ко многим» обычно представляет собой просто таблицу с двумя внешними ключами, с точки зрения SQL вы можете применить прием этого ответа. Но чтобы достичь этого только с помощью Django, я могу придумать один подход, аналогичный ответу @ozan, за исключением того, что первым шагом будет дублирование таблиц, участвующих в отношениях MTM (одна версия дубликатов в каждом приложении) , перенесите все внешние ключи в новое приложение и только затем удалите дубликаты в старом приложении. Отказ от ответственности: я не тестировал :)
Arnaud P
15

Как я это сделал (проверено на 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, ничего не изменилось, также, когда вы выполните $ python manage.py makemigrations, ничего нового не обнаружено.

  8. Теперь последний штрих: удалите Class Meta из app2.YourModel и выполните $ python manage.py makemigrations app2 && python manage.py migrate 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, а затем ответьте "Нет".

Майкл ван де Ваэтер
источник
3
Хотя это решение определенно «хакерское», чем у @ozan, и оно определенно требует большего редактирования, оно отлично сработало для меня (и это нормально для редактирования миграций - они должны быть редактируемыми, согласно документации).
pgcd 07
1
Возможно также использовать app_label = 'app1'мета-опцию.
Wtower
Genius! Это отлично сработало для меня в отношениях с ForeignKey. Я полагаю, что это также будет работать для полей ManyToMany.
Бабкен Варданян
1
Я выполнил ваши шаги, но поле в некоторой модели, принадлежащее app1, состоит из внешнего ключа с рекурсивной связью с перемещаемой моделью (myModel). Например, field1 = models.ForeignKey('app1.myModel').когда я мигрирую, я получаю ValueError о том, чтоfield1 was declared with a lazy reference to 'app1.myModel' but app 'app1' doesn't provide model 'MyModel'
Deesha 01
12

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

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

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

  5. На самом деле мы не хотим (или не должны) ничего делать со столом. Нам просто нужно, чтобы Django поверил, что изменение было внесено. Ответ Per @ Ozan, это делает 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поэтому нам нужно убедиться, что они отделены от операций с таблицами.

ПРИМЕЧАНИЕ: Django 2.2 добавил предупреждение ( models.E028), которое нарушает этот метод. Возможно, вы сможете обойти это, managed=Falseно я не тестировал его.

«Минимальный» набор операций различается в зависимости от ситуации, но следующая процедура должна работать для большинства / всех ForeignKeyмиграций:

  1. Скопируйте модель из app1в app2, установите db_table, но НЕ меняйте ссылки FK.
  2. Запустите makemigrationsи завершите всю app2миграцию state_operations(см. Выше)
    • Как и выше, добавьте зависимость в app2 CreateTableпоследнюю 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 готов к работе. Новая модель указывает на старую таблицу, и миграции Django убедили ее, что все было перемещено соответствующим образом. Большое предостережение (из ответа @ Michael) заключается в том, что ContentTypeдля новой модели создается новое. Если вы связываете (например, с помощью ForeignKey) с типами контента, вам нужно будет создать миграцию для обновления ContentTypeтаблицы.

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

  1. Удалить db_tableмета-запись
  2. Запустите makemigrationsснова, чтобы сгенерировать переименование базы данных
  3. Отредактируйте последнюю миграцию и убедитесь, что она зависит от DeleteTableмиграции. Не похоже, что это должно быть необходимо, поскольку оно Deleteдолжно быть чисто логическим, но я столкнулся с ошибками (например app1_yourmodel, не существует), если я этого не сделаю.
claytond
источник
Это сработало отлично, спасибо! Я не думаю, что редактирование последней миграции имеет значение, поскольку в любом случае она находится внизу дерева зависимостей.
Джеймс Микин,
1
Хороший ответ! Я думаю, вам нужно добавить закрывающую скобку к migrations.SeparateDatabaseAndState, верно?
atm
Это сработало для меня. Я также не редактировал последнюю миграцию (шаг 3, самая последняя строка всего ответа), например @JamesMeakin, и она все еще работала нормально
Megawatt
во втором сценарии, с ФК, второй шаг для меня не удался с ошибкой, которая имеет смысл:table_name: (models.E028) db_table 'table_name' is used by multiple models: app1.Model, app2.Model.
Михай Замфир,
Я использовал эту процедуру пару раз. Если вы сравните документацию для 2.2 ( docs.djangoproject.com/en/2.2/ref/checks ) и 2.1 ( docs.djangoproject.com/en/2.1/ref/checks ), вы увидите, что она была добавлена ​​в 2.2. Возможно, с этим можно обойтись, managed=Falseно мне негде проверить.
Claytond
1

Еще одна хитрая альтернатива, если данные небольшие или слишком сложные, но все же их важно поддерживать, - это:

  • Получите фикстуры данных с помощью manage.py dumpdata
  • Правильно приступайте к изменению модели и миграции, не связывая изменения
  • Глобальная замена приборов со старой модели и названий приложений на новые
  • Загрузить данные с помощью manage.py loaddata
Wtower
источник
1

Скопировано из моего ответа на https://stackoverflow.com/a/47392970/8971048

Если вам нужно переместить модель, и у вас больше нет доступа к приложению (или вам не нужен доступ), вы можете создать новую операцию и рассмотреть возможность создания новой модели только в том случае, если перенесенная модель не существует.

В этом примере я передаю 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))
            ],
        ),
    ]
Гэл Сингер
источник
Пожалуйста, не добавляйте один и тот же ответ на несколько вопросов. Ответьте лучшим и отметьте остальные как дубликаты. См. Допустимо ли добавить дублирующий ответ на несколько вопросов?
Петтер Фриберг
0

Это примерно проверено, поэтому не забудьте сделать резервную копию своей БД !!!

Например, есть два приложения: 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

Вот и все!

Сергей Федосеев
источник
Обратите внимание, что этот код, вероятно, полезен только для django 1.7. Попробовать это в django 2.0 не получится. Это также означает, что использование этого механизма для перемещения моделей увеличивает накладные расходы на обслуживание при обновлении вашей версии django.
Paul in 't Hout
0

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

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

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

Webthusiast
источник
0

Допустим, вы перемещаете модель 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 для моей модели, которая могла бы помочь.

Примечательно, что в результате вы получите измененную историю миграции. Это не имеет значения, даже если у вас есть база данных с примененными исходными миграциями. Если и исходная, и перезаписанная миграции заканчиваются с одной и той же схемой базы данных, тогда такая перезапись должна быть в порядке.

akaariai
источник
0
  1. измените названия старых моделей на 'model_name_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. отключите свой производственный сайт на несколько минут, чтобы выполнить все миграции без вмешательства пользователей

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

tomcounsell
источник