Итак, около года назад я начал проект и, как и все новые разработчики, на самом деле не уделял слишком много внимания структуре, однако теперь я продвинулся дальше вместе с Django, стало появляться, что мой макет проекта, в основном, мои модели ужасны по структуре ,
У меня есть модели, которые в основном хранятся в одном приложении, и на самом деле большинство из этих моделей должны быть в своих собственных индивидуальных приложениях, я пытался решить эту проблему и переместить их на юг, но мне это показалось сложным и действительно сложным из-за внешних ключей и т. Д.
Однако из-за Django 1.7 и встроенной поддержки миграций есть ли лучший способ сделать это сейчас?
Ответы:
Я удаляю старый ответ, так как это может привести к потере данных. Как упоминал Озан , мы можем создать 2 миграции по одной в каждом приложении. Комментарии под этим сообщением относятся к моему старому ответу.
Первая миграция для удаления модели из 1-го приложения.
Отредактируйте файл миграции, чтобы включить эти операции.
Вторая миграция, которая зависит от первой миграции и создания новой таблицы во втором приложении. После перемещения кода модели во второе приложение
и отредактируйте файл миграции примерно так.
источник
./manage.py migrate
все закончится в хорошем состоянии. Подделка миграции вручную - это неправильный способ ИМО.Это довольно легко сделать с помощью
migrations.SeparateDatabaseAndState
. По сути, мы используем операцию с базой данных для переименования таблицы одновременно с двумя операциями с состоянием, чтобы удалить модель из истории одного приложения и создать ее в истории другого.Удалить из старого приложения
В миграции:
Добавить в новое приложение
Сначала скопируйте модель в файл model.py нового приложения, а затем:
Это приведет к миграции с простой
CreateModel
операцией в качестве единственной операции. Оберните это вSeparateDatabaseAndState
операцию, чтобы мы не пытались воссоздать таблицу. Также включите предыдущую миграцию в качестве зависимости:источник
Я столкнулся с той же проблемой. Ответ Озана мне очень помог, но, к сожалению, этого было недостаточно. Действительно, у меня было несколько ссылок ForeignKey на модель, которую я хотел перенести. После некоторой головной боли я нашел решение, поэтому решил опубликовать его, чтобы решить проблему с людьми.
Вам нужно еще 2 шага:
ForeignKey
ссылкиTheModel
наIntegerfield
. Тогда бегиpython manage.py makemigrations
ForeignKey(TheModel)
вместоIntegerField()
. Затем снова выполните миграции (python manage.py makemigrations
). Затем вы можете выполнить миграцию, и она должна работать (python manage.py migrate
)Надеюсь, поможет. Конечно, протестируйте его на месте, прежде чем пробовать в продакшене, чтобы избежать неприятных сюрпризов :)
источник
Как я это сделал (проверено на Django == 1.8, с postgres, так что, вероятно, также 1.7)
ситуация
app1.YourModel
но вы хотите, чтобы он перешел в: app2.YourModel
добавьте это в app2.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, ничего не изменилось, также, когда вы выполните $ python manage.py makemigrations, ничего нового не обнаружено.
Теперь последний штрих: удалите Class Meta из app2.YourModel и выполните $ python manage.py makemigrations app2 && python manage.py migrate app2 (если вы посмотрите на эту миграцию, вы увидите что-то вроде этого :)
table = None, означает, что будет использоваться имя таблицы по умолчанию, которым в данном случае будет app2_yourmodel.
PS во время миграции он увидит, что content_type app1.yourmodel был удален и может быть удален. Вы можете сказать «да», но только если вы этим не пользуетесь. В случае, если вы сильно зависите от того, чтобы FK для этого типа содержимого оставались неповрежденными, пока не отвечайте «да» или «нет», но войдите в базу данных вручную, удалите contentype app2.yourmodel и переименуйте contenttype app1. yourmodel на app2.yourmodel, а затем ответьте "Нет".
источник
app_label = 'app1'
мета-опцию.field1 = models.ForeignKey('app1.myModel').
когда я мигрирую, я получаю ValueError о том, чтоfield1 was declared with a lazy reference to 'app1.myModel' but app 'app1' doesn't provide model 'MyModel'
У меня нервные миграции ручного кодирования (как того требует ответ Озана ), поэтому следующее объединяет стратегии Озана и Майкла, чтобы минимизировать количество необходимого ручного кодирования:
makemigrations
.app1
вapp2
Как рекомендовано @Michael, мы указываем новую модель на старую таблицу базы данных, используя
db_table
опцию Meta в «новой» модели:Беги
makemigrations
. Это будет генерироватьсяCreateModel
inapp2
иDeleteModel
inapp1
. Технически эти миграции относятся к одной и той же таблице и будут удалять (включая все данные) и воссоздавать таблицу.На самом деле мы не хотим (или не должны) ничего делать со столом. Нам просто нужно, чтобы Django поверил, что изменение было внесено. Ответ Per @ Ozan, это делает
state_operations
флагSeparateDatabaseAndState
. Таким образом, мы оборачиваем всеmigrations
записи В ОБЕИХ ФАЙЛАХ МИГРАЦИИ сSeparateDatabaseAndState(state_operations=[...])
. Например,становится
Вам также необходимо убедиться, что новая «виртуальная»
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
миграций:app1
вapp2
, установитеdb_table
, но НЕ меняйте ссылки FK.makemigrations
и завершите всюapp2
миграциюstate_operations
(см. Выше)app2
CreateTable
последнююapp1
миграциюmodels.py
(НЕ удаляйте ее), чтобы она не конкурировала с импортированным классом.Запустите,
makemigrations
но НЕ оборачивайте ничегоstate_operations
(изменения FK действительно должны произойти). Добавьте зависимость во всеForeignKey
миграции (т.е.AlterField
) вCreateTable
миграцию вapp2
(вам понадобится этот список для следующего шага, поэтому следите за ними). Например:CreateModel
например,app2.migrations.0002_auto_<date>
и скопируйте имя этой миграции.Найдите все миграции, у которых есть ForeignKey для этой модели (например, выполнив поиск,
app2.YourModel
чтобы найти такие миграции, как:Добавьте
CreateModel
миграцию как зависимость:Удалите модели из
app1
makemigrations
и завершитеapp1
миграциюstate_operations
.ForeignKey
миграциям (т.е.AlterField
) из предыдущего шага (может включать миграции вapp1
иapp2
).DeleteTable
уже зависели отAlterField
миграций, поэтому мне не нужно было вручную применять их (т.е.Alter
раньшеDelete
).На данный момент Django готов к работе. Новая модель указывает на старую таблицу, и миграции Django убедили ее, что все было перемещено соответствующим образом. Большое предостережение (из ответа @ Michael) заключается в том, что
ContentType
для новой модели создается новое. Если вы связываете (например, с помощьюForeignKey
) с типами контента, вам нужно будет создать миграцию для обновленияContentType
таблицы.Я хотел очистить после себя (мета-параметры и имена таблиц), поэтому я использовал следующую процедуру (от @Michael):
db_table
мета-записьmakemigrations
снова, чтобы сгенерировать переименование базы данныхDeleteTable
миграции. Не похоже, что это должно быть необходимо, поскольку оноDelete
должно быть чисто логическим, но я столкнулся с ошибками (напримерapp1_yourmodel
, не существует), если я этого не сделаю.источник
table_name: (models.E028) db_table 'table_name' is used by multiple models: app1.Model, app2.Model.
managed=False
но мне негде проверить.Еще одна хитрая альтернатива, если данные небольшие или слишком сложные, но все же их важно поддерживать, - это:
источник
Скопировано из моего ответа на https://stackoverflow.com/a/47392970/8971048
Если вам нужно переместить модель, и у вас больше нет доступа к приложению (или вам не нужен доступ), вы можете создать новую операцию и рассмотреть возможность создания новой модели только в том случае, если перенесенная модель не существует.
В этом примере я передаю MyModel из old_app в myapp.
источник
Это примерно проверено, поэтому не забудьте сделать резервную копию своей БД !!!
Например, есть два приложения:
src_app
иdst_app
, мы хотим переместить модельMoveMe
изsrc_app
в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
миграция:Добавить зависимость от
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
. Затем запустите:Вот и все!
источник
Вы можете попробовать следующее (не проверено):
src_app
вdest_app
dest_app
; убедитесь, что миграция схемы зависит от последнейsrc_app
миграции ( https://docs.djangoproject.com/en/dev/topics/migrations/#migration-files )dest_app
, который копирует все данные изsrc_app
src_app
; убедитесь, что миграция схемы зависит от последней миграции (данных),dest_app
то есть миграции на шаге 3Обратите внимание, что вы будете копировать всю таблицу, а не перемещать ее, но в этом случае обоим приложениям не нужно будет касаться таблицы, принадлежащей другому приложению, что, на мой взгляд, более важно.
источник
Допустим, вы перемещаете модель 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 для моей модели, которая могла бы помочь.
Примечательно, что в результате вы получите измененную историю миграции. Это не имеет значения, даже если у вас есть база данных с примененными исходными миграциями. Если и исходная, и перезаписанная миграции заканчиваются с одной и той же схемой базы данных, тогда такая перезапись должна быть в порядке.
источник
Делайте это индивидуально для каждой модели, которую необходимо переместить. Я бы не советовал делать то, что говорится в другом ответе, переходя на целые числа и обратно на внешние ключи. Есть вероятность, что новые внешние ключи будут другими, а строки могут иметь разные идентификаторы после миграции, и я не хотел рисковать несоответствия идентификаторов при переключении обратно на внешние ключи.
источник