перевод статьи "How to Move a Django Model to Another App"
Если вы когда-нибудь задумывались о рефакторинге своего приложение Django, то, возможно, вам приходилось перемещать модель Django. Есть несколько способов перенестимодель Django из одного приложения в другое с помощью миграции Django, но, к сожалению, ни один из них не является простым.
Перемещение моделей между приложениями Django обычно является очень сложной задачей, которая включает в себя копирование данных, изменение ограничений и переименование объектов. Из-за этих сложностей объектно-реляционный преобразователь (ORM) Django не предоставляет встроенных операций миграции, которые могут обнаруживать и автоматизировать весь процесс. Вместо этого ORM предоставляет набор низкоуровневых операций миграции, которые позволяют разработчикам Django самостоятельно реализовать процесс в среде миграции.
В этом руководстве вы узнаете:
Как перенести модель Django из одного приложения в другое
Как использовать расширенные функции интерфейса командной строки (CLI) миграции Django, такие как sqlmigrate, showmigrations и sqlsequencereset
Как составить и проверить план миграции
Как сделать миграцию обратимой и как отменить миграцию
Что такое интроспекция и как Django использует ее при миграции
После изучения этого руководства вы сможете выбрать лучший подход для переноса модели Django из одного приложения в другое в зависимости от вашего конкретного варианта использования.
Пример случая: перемещение модели Django в другое приложение
В этом руководстве вы будете работать над приложением для магазина. Ваш магазин будет запущен с двумя приложениями Django:
catalog: Это приложение предназначено для хранения данных о товарах и категориях товаров.
продажа: это приложение предназначено для записи и отслеживания продаж товаров.
После того, как вы закончите настройку этих двух приложений, вы собираетесь переместить модель Django под названием Product в новое приложение под названием product. В процессе вы столкнетесь со следующими проблемами:
Перемещаемая модель имеет отношения внешнего ключа с другими моделями.
Другие модели имеют отношения внешнего ключа с перемещаемой моделью.
Перемещаемая модель имеет индекс в одном из полей (помимо первичного ключа).
Эти задачи вызваны реальными процессами рефакторинга. После того, как вы их преодолеете, вы будете готовы спланировать аналогичный процесс миграции для вашего конкретного варианта использования.
Настройка: подготовьте среду
Прежде чем вы начнете перемещать вещи, вам нужнонастроить начальное состояние вашего проекта. В этом руководстве используется Django 3, работающий на Python 3.8, но вы можете использовать аналогичные методы в других версиях.
Настройка виртуальной среды Python
Сначала создайте виртуальную среду в новом каталоге:
$ mkdir django-move-model-experiment
$ cd django-move-model-experiment
$ python -m venv venv
Для получения пошаговых инструкций по созданию виртуальной среды ознакомьтесь сPython Virtual Environments: A Primer.
Создать проект Django
В вашем терминале активируйте виртуальную среду и установите Django:
$ source venv/bin/activate
$ pip install django
Collecting django
Collecting pytz (from django)
Collecting asgiref~=3.2 (from django)
Collecting sqlparse>=0.2.2 (from django)
Installing collected packages: pytz, asgiref, sqlparse, django
Successfully installed asgiref-3.2.3 django-3.0.4 pytz-2019.3 sqlparse-0.3.1
Теперь вы готовы создать свой проект Django. Используйте django-admin startproject для создания проекта под названием django-move-model-эксперимент:
$ django-admin startproject django-move-model-experiment
$ cd django-move-model-experiment
После выполнения этой команды вы увидите, что Django создал новые файлы и каталоги. Дополнительные сведения о том, как начать новый проект Django, см. В разделеЗапуск проекта Django.
Создание приложений Django
Теперь, когда у вас есть свежий проект Django, создайте приложение с каталогом товаров вашего магазина:
$ python manage.py startapp catalog
Затем добавьте в новое приложение каталога следующие модели:
# catalog/models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100)
class Product(models.Model):
name = models.CharField(max_length=100, db_index=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
Вы успешно создали модели категорий и продуктов в своем приложении-каталоге. Теперь, когда у вас есть каталог, вы хотите начать продавать свою продукцию. Создайте еще одно приложение для продаж:
$ python manage.py startapp sale
Добавьте следующую модель продажи в новое приложение продажи:
# sale/models.py
from django.db import models
from catalog.models import Product
class Sale(models.Model):
created = models.DateTimeField()
product = models.ForeignKey(Product, on_delete=models.PROTECT)
Обратите внимание, что модель Sale ссылается на модель Product с помощьюForeignKey.
Сгенерируйте и примените начальные миграции
Чтобы завершить настройку, сгенерируйтемиграции и примените их:
$ python manage.py makemigrations catalog sale
Migrations for 'catalog':
catalog/migrations/0001_initial.py
- Create model Category
- Create model Product
Migrations for 'sale':
sale/migrations/0001_initial.py
- Create model Sale
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, catalog, contenttypes, sale, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying catalog.0001_initial... OK
Applying sale.0001_initial... OK
Applying sessions.0001_initial... OK
Чтобы узнать больше о миграции Django, ознакомьтесь сDjango Migrations: A Primer. После выполнения миграции вы готовы создать образцы данных!
Создать образец данных
Чтобы сделать сценарий миграции максимально реалистичным, активируйтеоболочку Django из окна терминала:
$ python manage.py shell
Далее создайте следующие объекты:
>>> from catalog.models import Category, Product
>>> clothes = Category.objects.create(name='Clothes')
>>> shoes = Category.objects.create(name='Shoes')
>>> Product.objects.create(name='Pants', category=clothes)
>>> Product.objects.create(name='Shirt', category=clothes)
>>> Product.objects.create(name='Boots', category=shoes)
Вы создали две категории: «Обувь» и «Одежда». Затем вы добавили два продукта, «Штаны» и «Рубашку», в категорию «Одежда» и один продукт, «Ботинки», в категорию «Обувь».
Поздравляю! Вы завершили настройку начального состояния вашего проекта. В реальной жизни именно здесь вы должны начать планировать рефакторинг. Каждый из трех подходов, представленных в этом руководстве, начнется с этого момента.
Долгий путь: копирование данных в новую модель Django
Для начала вам предстоит долгий путь:
Создать новую модель
Скопируйте в него данные
Отбросьте старую таблицу
У этого подхода есть некоторые подводные камни, о которых вам следует знать. Вы подробно изучите их в следующих разделах.
Создать новую модель
Начните с создания нового продукта-приложения. На своем терминале выполните следующую команду:
$ python manage.py startapp product
После выполнения этой команды вы заметите, что в проект был добавлен новый каталог с именем product.
Чтобы зарегистрировать новое приложение в существующем проекте Django, добавьте его в список INSTALLED_APPS в файле settings.py Django:
--- a/store/store/settings.py
+++ b/store/store/settings.py
@@ -40,6 +40,7 @@ INSTALLED_APPS = [
'catalog',
'sale',
+ 'product',
]
MIDDLEWARE = [
Ваш новый продукт-приложение теперь зарегистрировано в Django. Затем создайте модель продукта в новом приложении продукта. Вы можете скопировать код из приложения каталога:
# product/models.py
from django.db import models
from catalog.models import Category
class Product(models.Model):
name = models.CharField(max_length=100, db_index=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
Теперь, когда вы определили модель, попробуйте сгенерировать для нее миграции:
$ python manage.py makemigrations product
SystemCheckError: System check identified some issues:
ERRORS:
catalog.Product.category: (fields.E304) Reverse accessor for 'Product.category' clashes with reverse accessor for 'Product.category'.
HINT: Add or change a related_name argument to the definition for 'Product.category' or 'Product.category'.
product.Product.category: (fields.E304) Reverse accessor for 'Product.category' clashes with reverse accessor for 'Product.category'.
HINT: Add or change a related_name argument to the definition for 'Product.category' or 'Product.category'.
Ошибка говорит о том, что Django обнаружил две модели с одним и тем же обратным аксессором для категории поля. Это связано с тем, что есть две модели с именем Product, которые ссылаются на модель категории, создавая конфликт.
Когда вы добавляете внешние ключи в свою модель, Django создает обратный метод доступа в связанной модели. В данном случае обратный аксессуар - это товары. Обратный метод доступа позволяет получить доступ к связанным объектам, например, категории: category.products.
Новая модель - это та, которую вы хотите сохранить, поэтому для разрешения этого конфликта удалите обратный аксессор из старой модели в catalog / models.py:
--- a/store/catalog/models.py
+++ b/store/catalog/models.py
@@ -7,4 +7,4 @@ class Category(models.Model):
class Product(models.Model):
name = models.CharField(max_length=100, db_index=True)
- category = models.ForeignKey(Category, on_delete=models.CASCADE)
+ category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='+')
Атрибут related_name может использоваться для явной установки связанного имени для обратного метода доступа. Здесь вы используете специальное значение +, которое указывает Django не создавать обратный метод доступа.
Теперь сгенерируйте миграцию для приложения каталога:
$ python manage.py makemigrations catalog
Migrations for 'catalog':
catalog/migrations/0002_auto_20200124_1250.py
- Alter field category on product
Пока не применяйте эту миграцию! Как только это изменение произойдет, код, который использовал обратный метод доступа, может сломаться.
Теперь, когда нет конфликта между обратными методами доступа, попробуйте сгенерировать миграции для нового приложения продукта:
$ python manage.py makemigrations product
Migrations for 'product':
product/migrations/0001_initial.py
- Create model Product
Отлично! Вы готовы перейти к следующему шагу.
Скопируйте данные в новую модель
На предыдущем шаге вы создали новое приложение продукта с моделью продукта, идентичной модели, которую вы хотите переместить. Следующий шаг - переместить данные из старой модели в новую.
Чтобы создать миграцию данных, выполните следующую команду на своем терминале:
$ python manage.py makemigrations product --empty
Migrations for 'product':
product/migrations/0002_auto_20200124_1300.py
Отредактируйте новый файл миграции и добавьте операцию для копирования данных из старой таблицы:
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('product', '0001_initial'),
]
operations = [
migrations.RunSQL("""
INSERT INTO product_product (
id,
name,
category_id
)
SELECT
id,
name,
category_id
FROM
catalog_product;
""", reverse_sql="""
INSERT INTO catalog_product (
id,
name,
category_id
)
SELECT
id,
name,
category_id
FROM
product_product;
""")
]
Чтобы выполнить SQL в миграции, вы используете специальную команду миграцииRunSQL. Первый аргумент - это SQL, который нужно применить. Вы также предоставляете действие для отмены миграции с помощью аргумента reverse_sql.
Отмена миграции может пригодиться, когда вы обнаружите ошибку и захотите отменить изменение. Большинство встроенных действий по миграции можно отменить. Например, обратное действие для добавления поля - это его удаление. Обратное действие для создания новой таблицы - это ее отбрасывание. Обычно лучше всего предоставить в RunSQL reverse_SQL, чтобы можно было вернуться, если что-то пойдет не так.
В этом случае операция прямой миграции вставляет данные из product_product в catalog_product. Обратная операция будет делать прямо противоположное, вставляя данные из catalog_product в product_product. Предоставив Django обратную операцию, вы сможете отменить миграцию в случае аварии.
На данный момент вы еще наполовину завершили процесс миграции. Но из этого следует извлечь урок, поэтому примените миграции:
$ python manage.py migrate product
Operations to perform:
Apply all migrations: product
Running migrations:
Applying product.0001_initial... OK
Applying product.0002_auto_20200124_1300... OK
Прежде чем перейти к следующему шагу, попробуйте создать новый товар:
>>> from product.models import Product
>>> Product.objects.create(name='Fancy Boots', category_id=2)
Traceback (most recent call last):
File "/venv/lib/python3.8/site-packages/django/db/backends/utils.py", line 86, in _execute
return self.cursor.execute(sql, params)
psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "product_product_pkey"
DETAIL: Key (id)=(1) already exists.
Когда вы используете автоинкрементный первичный ключ,Django создает последовательность в базе данных для присвоения уникальных идентификаторов новым объектам. Обратите внимание, например, что вы не указали идентификатор для нового продукта. Обычно вы не хотите указывать идентификатор, потому что вы хотите, чтобы база данных назначила вам первичные ключи с помощью последовательности. Однако в этом случае новая таблица дала новому продукту идентификатор 1, хотя этот идентификатор уже существовал в таблице.
Итак, что пошло не так? Когда вы копировали данные в новую таблицу, вы не синхронизировали последовательность. Чтобы синхронизировать последовательность, вы можете использовать другую команду администратора Django под названиемsqlsequencereset. Команда создает сценарий для установки текущего значения последовательности на основе существующих данных в таблице. Эта команда часто используется для заполнения новых моделей уже существующими данными.
Используйте sqlsequencereset для создания сценария для синхронизации последовательности:
$ python manage.py sqlsequencereset product
BEGIN;
SELECT setval(pg_get_serial_sequence('"product_product"','id'), coalesce(max("id"), 1), max("id") IS NOT null)
FROM "product_product";
COMMIT;
Сценарий, созданный командой, зависит от базы данных. В данном случае это база данных PostgreSQL. Сценарий устанавливает текущее значение последовательности равным следующему значению, которое должна выдать последовательность, которое является максимальным идентификатором в таблице плюс один.
В завершение добавьте фрагмент в перенос данных:
--- a/store/product/migrations/0002_auto_20200124_1300.py
+++ b/store/product/migrations/0002_auto_20200124_1300.py
@@ -22,6 +22,8 @@ class Migration(migrations.Migration):
category_id
FROM
catalog_product;
+
+ SELECT setval(pg_get_serial_sequence('"product_product"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "product_product";
""", reverse_sql="""
INSERT INTO catalog_product (
id,
Фрагмент синхронизирует последовательность при применении миграции, решая проблему последовательности, с которой вы столкнулись выше.
Этот обходной путь для изучения последовательностей синхронизации привел к небольшому беспорядку в вашем коде. Чтобы очистить его, удалите данные в новой модели из оболочки Django:
>>> from product.models import Product
>>> Product.objects.all().delete()
(3, {'product.Product': 3})
Теперь, когда скопированные данные удалены, вы можете отменить миграцию. Чтобы отменить миграцию, вы переходите к предыдущей миграции:
$ python manage.py showmigrations product
product
[X] 0001_initial
[X] 0002_auto_20200124_1300
$ python manage.py migrate product 0001_initial
Operations to perform:
Target specific migration: 0001_initial, from product
Running migrations:
Rendering model states... DONE
Unapplying product.0002_auto_20200124_1300... OK
Сначала вы использовали команду showmigrations, чтобы перечислить миграции, примененные к продукту приложения. Выходные данные показывают, что были применены обе миграции. Затем вы отменили миграцию 0002_auto_20200124_1300 by migrating to the prior migration, 0001_initial.
Если вы снова выполните showmigrations, то увидите, что вторая миграция больше не помечена как примененная:
$ python manage.py showmigrations product
product
[X] 0001_initial
[ ] 0002_auto_20200124_1300
Пустое поле подтверждает, что вторая миграция была отменена. Теперь, когда у вас есть чистый лист, запустите миграции с новым кодом:
$ python manage.py migrate product
Operations to perform:
Apply all migrations: product
Running migrations:
Applying product.0002_auto_20200124_1300... OK
Выполняемые операции:
Применить все миграции: продукт
Выполняемые миграции:
>>> from product.models import Product
>>> Product.objects.create(name='Fancy Boots', category_id=2)
Перенос был успешно применен. Убедитесь, что теперь вы можете создать новый Продукт в оболочке Django:
Обновите внешние ключи до новой модели
В старой таблице в настоящее время есть другие таблицы, ссылающиеся на нее с помощью поля ForeignKey. Прежде чем вы сможете удалить старую модель, вам необходимо изменить модели, ссылающиеся на старую модель, чтобы они ссылались на новую модель.
Одна модель, которая все еще ссылается на старую модель, - это Sale в приложении продажи. Измените внешний ключ в модели продажи, чтобы он ссылался на новую модель продукта:
--- a/store/sale/models.py
+++ b/store/sale/models.py
@@ -1,6 +1,6 @@
from django.db import models
-from catalog.models import Product
+from product.models import Product
class Sale(models.Model):
created = models.DateTimeField()
Создайте миграцию и примените ее:
$ python manage.py makemigrations sale
Migrations for 'sale':
sale/migrations/0002_auto_20200124_1343.py
- Alter field product on sale
$ python manage.py migrate sale
Operations to perform:
Apply all migrations: sale
Running migrations:
Applying sale.0002_auto_20200124_1343... OK
Удалить старую модель
На предыдущем шаге были удалены все ссылки на старую модель продукта. Теперь можно безопасно удалить старую модель из приложения-каталога:
--- a/store/catalog/models.py
+++ b/store/catalog/models.py
@@ -3,8 +3,3 @@ from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100)
-
-
-class Product(models.Model):
- name = models.CharField(max_length=100, db_index=True)
- category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='+')
Миграции для каталога:
$ python manage.py makemigrations
Migrations for 'catalog':
catalog/migrations/0003_delete_product.py
- Delete model Product
- Удалить модель продукта
Чтобы удалить старую модель только после копирования данных, добавьте следующую зависимость:
--- a/store/catalog/migrations/0003_delete_product.py
+++ b/store/catalog/migrations/0003_delete_product.py
@@ -7,6 +7,7 @@ class Migration(migrations.Migration):
dependencies = [
('catalog', '0002_auto_20200124_1250'),
+ ('sale', '0002_auto_20200124_1343'),
]
operations = [
Добавление этой зависимости чрезвычайно важно. Пропуск этого шага может иметь ужасные последствия, включая потерю данных. Чтобы узнать больше о файлах миграции и зависимостях между миграциями, прочтите статью«Копаем глубже в миграции Django».
Примечание. Имя миграции включает дату и время ее создания. Если вы следуете своему собственному коду, эти части имени будут другими.
Теперь, когда вы добавили зависимость, примените миграцию:
Выполняемые операции:
Применить все миграции: каталог
Выполняемые миграции:
$ python manage.py migrate catalog
Operations to perform:
Apply all migrations: catalog
Running migrations:
Applying catalog.0003_delete_product... OK
Перенос завершен! Вы успешно переместили модель продукта из приложения-каталога в приложение для нового продукта, создав новую модель и скопировав в нее данные.
Бонус: обратить вспять миграции
Одним из преимуществ миграции Django является то, что она обратима. Что означает обратимость миграции? Если вы допустили ошибку, вы можете отменить миграцию, и база данных вернется в состояние, в котором она была до выполнения миграции.
Помните, как вы ранее предоставляли reverse_sql для RunSQL? Что ж, здесь это окупается.
Примените все миграции к новой базе данных:
Выполняемые операции:
Применить все миграции: admin, auth, catalog, contenttypes, product, sale, sessions
Выполняемые миграции:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, catalog, contenttypes, product, sale, sessions
Running migrations:
Applying product.0001_initial... OK
Applying product.0002_auto_20200124_1300... OK
Applying sale.0002_auto_20200124_1343... OK
Applying catalog.0003_delete_product... OK
Теперь переверните их все, используяпециальное ключевое слово zero:с
$ python manage.py migrate product zero
Operations to perform:
Unapply all migrations: product
Running migrations:
Rendering model states... DONE
Unapplying catalog.0003_delete_product... OK
Unapplying sale.0002_auto_20200124_1343... OK
Unapplying product.0002_auto_20200124_1300... OK
Unapplying product.0001_initial... OK
База данных вернулась в исходное состояние. Если вы развернете эту версию и обнаружите ошибку, вы можете исправить ее!
Удалить объявления
Обработка особых случаев
Когда вы перемещаете модели из одного приложения в другое, некоторые функции Django могут потребовать особого внимания. В частности, добавление или изменение ограничений базы данных и использование общих отношений требуют особой осторожности.
Изменение ограничений
Добавление ограничений в таблицы с данными может быть опасной операцией для действующей системы. Чтобы добавить ограничение, база данных должна сначала его проверить. Во время проверки база данных устанавливает блокировку таблицы, которая может препятствовать другим операциям до завершения процесса.
Некоторые ограничения, такие как NOT NULL и CHECK, могут потребовать полного сканирования таблицы для проверки допустимости новых данных. Другие ограничения, такие как FOREIGN KEY, требуют проверки с другой таблицей, что может занять некоторое время в зависимости от размера таблицы, на которую указывает ссылка.
Обработка общих отношений
Если вы используете общие отношения, вам может потребоваться дополнительный шаг. Общие отношения используют как первичный ключ, так и идентификатор типа контента модели для ссылки на строку в любой таблице модели. У старой и новой модели разные идентификаторы типа контента, поэтому общие соединения могут прерваться. Иногда это может остаться незамеченным, потому что целостность общих внешних ключей не обеспечивается базой данных.
Есть два способа работать с универсальными внешними ключами:
1. Обновите идентификатор типа контента новой модели до идентификатора старой модели.
2. Обновите идентификатор типа контента всех ссылающихся таблиц до идентификатора новой модели.
В любом случае, убедитесь, что вы правильно протестировали его перед развертыванием в производственной среде.
Резюме: плюсы и минусы копирования данных
Перенос модели Django в другое приложение путем копирования данных имеет свои преимущества и недостатки. Вот некоторые из плюсов, связанных с этим подходом:
Он поддерживается ORM: выполнение этого перехода с использованием встроенных операций миграции гарантирует надлежащую поддержку базы данных.
Это обратимо: при необходимости можно отменить миграцию.
Вот некоторые из минусов, связанных с этим подходом:
Это медленно: копирование больших объемов данных может занять время.
Это требует простоя: изменение данных в старой таблице во время копирования в новую таблицу приведет к потере данных во время перехода. Чтобы этого не произошло, необходим простой.
Для синхронизации базы данных требуется ручная работа: загрузка данных в существующие таблицы требует синхронизации последовательностей и общих внешних ключей.
Как вы увидите в следующих разделах, использование этого подхода для переноса модели Django в другое приложение занимает гораздо больше времени, чем другие подходы.
Краткий путь: ссылка новой модели Django на старую таблицу
В предыдущем подходе вы скопировали все данные в новую таблицу. Миграция требовала простоя, и ее выполнение могло занять много времени в зависимости от того, сколько данных нужно скопировать.
Что, если вместо копирования данных вы измените новую модель так, чтобы она ссылалась на старую таблицу?
Создать новую модель
На этот раз вы собираетесь внести все изменения в модели сразу, а затем позволить Django сгенерировать все миграции.
Сначала удалите модель продукта из приложения каталога:
--- a/store/catalog/models.py
+++ b/store/catalog/models.py
@@ -3,8 +3,3 @@ from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100)
-
-
-class Product(models.Model):
- name = models.CharField(max_length=100, db_index=True)
- category = models.ForeignKey(Category, on_delete=models.CASCADE)
Вы удалили модель продукта из приложения-каталога. Теперь переместите модель продукта в новое приложение продукта:
# store/product/models.py
from django.db import models
from catalog.models import Category
class Product(models.Model):
name = models.CharField(max_length=100, db_index=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
Теперь, когда модель продукта существует в приложении продукта, вы можете изменить любые ссылки на старую модель продукта, чтобы ссылаться на новую модель продукта. В этом случае вам нужно будет изменить внешний ключ в продаже на ссылку на продукт.
--- a/store/sale/models.py
+++ b/store/sale/models.py
@@ -1,6 +1,6 @@
from django.db import models
-from catalog.models import Product
+from product.models import Product
class Sale(models.Model):
created = models.DateTimeField()
Прежде чем перейти к генерации миграций, вам необходимо внести еще одно небольшое изменение в новую модель продукта:
--- a/store/product/models.py
+++ b/store/product/models.py
@@ -5,3 +5,6 @@ from catalog.models import Category
class Product(models.Model):
name = models.CharField(max_length=100, db_index=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
+
+ class Meta:
+ db_table = 'catalog_product'
В моделях Django есть мета-опцияdb_table. С помощью этой опции вы можете указать имя таблицы, которое будет использоваться вместо имени, созданного Django. Этот параметр чаще всего используется при настройке ORM в существующей схеме базы данных, в которой имена таблиц не соответствуют соглашению об именах Django.
В этом случае вы устанавливаете имя таблицы в приложении продукта для ссылки на существующую таблицу в приложении каталога.
Чтобы завершить настройку, сгенерируйте миграции:
$ python manage.py makemigrations sale product catalog
Migrations for 'catalog':
catalog/migrations/0002_remove_product_category.py
- Remove field category from product
catalog/migrations/0003_delete_product.py
- Delete model Product
Migrations for 'product':
product/migrations/0001_initial.py
- Create model Product
Migrations for 'sale':
sale/migrations/0002_auto_20200104_0724.py
- Alter field product on sale
Прежде чем двигаться дальше, составьте план миграции с флагом--plan:
$ python manage.py migrate --plan
Planned operations:
catalog.0002_remove_product_category
Remove field category from product
product.0001_initial
Create model Product
sale.0002_auto_20200104_0724
Alter field product on sale
catalog.0003_delete_product
Delete model Product
В выводе команды указан порядок, в котором Django будет применять миграции.
Устранение изменений в базе данных
Основное преимущество этого подхода заключается в том, что вы фактически не вносите никаких изменений в базу данных, а только в код. Чтобы исключить изменения в базе данных, вы можете использовать специальную операцию миграцииSeparateDatabaseAndState.
SeparateDatabaseAndState можно использовать для изменения действий, которые Django выполняет во время миграции. Чтобы узнать больше о том, как использовать SeparateDatabaseAndState, прочтите статьюКак создать индекс в Django без простоев..
Если вы посмотрите на содержимое миграций, сгенерированных Django, то увидите, что Django создает новую модель и удаляет старую. Если вы выполните эти миграции, данные будут потеряны, а таблица будет создана пустой. Чтобы этого избежать, вам нужно убедиться, что Django не вносит никаких изменений в базу данных во время миграции.
Вы можете исключить изменения в базе данных, заключив каждую операцию миграции в операцию SeparateDatabaseAndState. Чтобы указать Django не применять никаких изменений к базе данных, вы можете установить для db_operations пустой список.
Вы планируете повторно использовать старую таблицу, поэтому вам нужно предотвратить ее удаление Django. Перед тем как удалить модель, Django удалит поля, ссылающиеся на модель. Итак, во-первых, предотвратите передачу Django внешнего ключа из продажи в продукт:
--- a/store/catalog/migrations/0002_remove_product_category.py
+++ b/store/catalog/migrations/0002_remove_product_category.py
@@ -10,8 +10,14 @@ class Migration(migrations.Migration):
]
operations = [
- migrations.RemoveField(
- model_name='product',
- name='category',
+ migrations.SeparateDatabaseAndState(
+ state_operations=[
+ migrations.RemoveField(
+ model_name='product',
+ name='category',
+ ),
+ ],
+ # You're reusing the table, so don't drop it
+ database_operations=[],
),
]
Теперь, когда Django обработал связанные объекты, он может отбросить модель. Вы хотите сохранить таблицу Product, чтобы предотвратить ее удаление Django:
--- a/store/catalog/migrations/0003_delete_product.py
+++ b/store/catalog/migrations/0003_delete_product.py
@@ -11,7 +11,13 @@ class Migration(migrations.Migration):
]
operations = [
- migrations.DeleteModel(
- name='Product',
- ),
+ migrations.SeparateDatabaseAndState(
+ state_operations=[
+ migrations.DeleteModel(
+ name='Product',
+ ),
+ ],
+ # You want to reuse the table, so don't drop it
+ database_operations=[],
+ )
]
Вы использовали database_operations = [], чтобы предотвратить удаление таблицы Django. Затем запретите Django создавать новую таблицу:
--- a/store/product/migrations/0001_initial.py
+++ b/store/product/migrations/0001_initial.py
@@ -13,15 +13,21 @@ class Migration(migrations.Migration):
]
operations = [
- migrations.CreateModel(
- name='Product',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(db_index=True, max_length=100)),
- ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='catalog.Category')),
+ migrations.SeparateDatabaseAndState(
+ state_operations=[
+ migrations.CreateModel(
+ name='Product',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(db_index=True, max_length=100)),
+ ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='catalog.Category')),
+ ],
+ options={
+ 'db_table': 'catalog_product',
+ },
+ ),
],
- options={
- 'db_table': 'catalog_product',
- },
- ),
+ # You reference an existing table
+ database_operations=[],
+ )
]
Здесь вы использовали database_operations = [], чтобы предотвратить создание Django новой таблицы. Наконец, вы хотите, чтобы Django не воссоздавал ограничение внешнего ключа из Sale в новую модель продукта. Поскольку вы повторно используете старую таблицу, ограничение остается в силе:
--- a/store/sale/migrations/0002_auto_20200104_0724.py
+++ b/store/sale/migrations/0002_auto_20200104_0724.py
@@ -12,9 +12,14 @@ class Migration(migrations.Migration):
]
operations = [
- migrations.AlterField(
- model_name='sale',
- name='product',
- field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='product.Product'),
+ migrations.SeparateDatabaseAndState(
+ state_operations=[
+ migrations.AlterField(
+ model_name='sale',
+ name='product',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='product.Product'),
+ ),
+ ],
+ database_operations=[],
),
]
Теперь, когда вы закончили редактировать файлы миграции, примените миграции:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, catalog, contenttypes, product, sale, sessions
Running migrations:
Applying catalog.0002_remove_product_category... OK
Applying product.0001_initial... OK
Applying sale.0002_auto_20200104_0724... OK
Applying catalog.0003_delete_product... OK
На этом этапе ваша новая модель указывает на старую таблицу. Django не вносил никаких изменений в базу данных, и все изменения были внесены в состояние модели Django в коде. Но прежде чем называть это успехом и двигаться дальше, стоит убедиться, что состояние новой модели совпадает с состоянием базы данных.
Бонус: внесите изменения в новую модель
Чтобы убедиться, что состояние моделей соответствует состоянию базы данных, попробуйте внести изменения в новую модель и убедитесь, что Django правильно ее обнаруживает.
Модель продукта определяет индекс в поле имени. Удалите этот индекс:
--- a/store/product/models.py
+++ b/store/product/models.py
@@ -3,7 +3,7 @@ from django.db import models
from catalog.models import Category
class Product(models.Model):
- name = models.CharField(max_length=100, db_index=True)
+ name = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
class Meta:
Вы удалили индекс, исключив db_index = True. Затем сгенерируйте миграцию:
$ python manage.py makemigrations
Migrations for 'product':
product/migrations/0002_auto_20200104_0856.py
- Alter field name on product
Прежде чем двигаться дальше, проверьте SQL, сгенерированный Django для этой миграции:
$ python manage.py sqlmigrate product 0002
BEGIN;
--
-- Alter field name on product
--
DROP INDEX IF EXISTS "catalog_product_name_924af5bc";
DROP INDEX IF EXISTS "catalog_product_name_924af5bc_like";
COMMIT;
Большой! Django обнаружил старый индекс, о чем свидетельствует префикс «catalog_ *». Теперь вы можете выполнить миграцию:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, catalog, contenttypes, product, sale, sessions
Running migrations:
Applying product.0002_auto_20200104_0856... OK
Убедитесь, что вы получили ожидаемый результат в базе данных:
django_migration_test=# \d catalog_product
Table "public.catalog_product"
Column | Type | Nullable | Default
-------------+------------------------+----------+---------------------------------------------
id | integer | not null | nextval('catalog_product_id_seq'::regclass)
name | character varying(100) | not null |
category_id | integer | not null |
Indexes:
"catalog_product_pkey" PRIMARY KEY, btree (id)
"catalog_product_category_id_35bf920b" btree (category_id)
Foreign-key constraints:
"catalog_product_category_id_35bf920b_fk_catalog_category_id"
FOREIGN KEY (category_id) REFERENCES catalog_category(id)
DEFERRABLE INITIALLY DEFERRED
Referenced by:
TABLE "sale_sale" CONSTRAINT "sale_sale_product_id_18508f6f_fk_catalog_product_id"
FOREIGN KEY (product_id) REFERENCES catalog_product(id)
DEFERRABLE INITIALLY DEFERRED
Успех! Индекс в столбце имени был удален.
Резюме: плюсы и минусы изменения ссылки на модель
Изменение модели для ссылки на другую модель имеет свои преимущества и недостатки. Вот некоторые из плюсов, связанных с этим подходом:
Это быстро: этот подход не вносит никаких изменений в базу данных, поэтому он выполняется очень быстро.
Он не требует простоев: этот подход не требует копирования данных, поэтому его можно выполнять в действующей системе без простоев.
Это обратимо: при необходимости эту миграцию можно отменить.
Он поддерживается ORM: выполнение этого перехода с использованием встроенных операций миграции гарантирует надлежащую поддержку базы данных.
Он не требует никакой синхронизации с базой данных: при таком подходе связанные объекты, такие как индексы и последовательности, остаются неизменными.
Единственный серьезный недостаток этого подхода в том, что он нарушает соглашение об именах. Использование существующей таблицы означает, что в таблице по-прежнему будет использоваться имя старого приложения.
Обратите внимание, насколько этот подход намного проще, чем копирование данных.
Путь Django: переименовать таблицу
В предыдущем примере вы заставили новую модель ссылаться на старую таблицу в базе данных. В результате вы нарушили соглашение об именах, используемое Django. В этом подходе вы делаете обратное: вы заставляете старую таблицу ссылаться на новую модель.
В частности, вы создаете новую модель и генерируете для нее миграцию. Затем вы берете имя новой таблицы из созданной Django миграции и, вместо создания таблицы для новой модели, вы переименовываете старую таблицу в имя новой таблицы, используя специальную операцию миграции AlterModelTable.
Создать новую модель
Как и раньше, вы начинаете с создания нового продукта, чтобы внести все изменения сразу. Сначала удалите модель продукта из приложения каталога:
--- a/store/catalog/models.py
+++ b/store/catalog/models.py
@@ -3,8 +3,3 @@ from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100)
-
-
-class Product(models.Model):
- name = models.CharField(max_length=100, db_index=True)
- category = models.ForeignKey(Category, on_delete=models.CASCADE)
Вы исключили Товар из каталога. Затем переместите модель продукта в новое приложение продукта:
# store/product/models.py
from django.db import models
from catalog.models import Category
class Product(models.Model):
name = models.CharField(max_length=100, db_index=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
Модель продукта теперь существует в вашем приложении продукта. Теперь измените внешний ключ в разделе Распродажа на ссылку на продукт.
--- a/store/sale/models.py
+++ b/store/sale/models.py
@@ -1,6 +1,6 @@
from django.db import models
-from catalog.models import Product
+from product.models import Product
class Sale(models.Model):
created = models.DateTimeField()
--- a/store/store/settings.py
+++ b/store/store/settings.py
@@ -40,6 +40,7 @@ INSTALLED_APPS = [
'catalog',
'sale',
+ 'product',
]
Затем позвольте Django сгенерировать для вас миграции:
$ python manage.py makemigrations sale catalog product
Migrations for 'catalog':
catalog/migrations/0002_remove_product_category.py
- Remove field category from product
catalog/migrations/0003_delete_product.py
- Delete model Product
Migrations for 'product':
product/migrations/0001_initial.py
- Create model Product
Migrations for 'sale':
sale/migrations/0002_auto_20200110_1304.py
- Alter field product on sale
Вы хотите, чтобы Django не удалял таблицу из-за того, что вы собираетесь ее переименовать.
Чтобы получить имя модели продукта в приложении продукта, сгенерируйте SQL для миграции, которая создает продукт:
$ python manage.py sqlmigrate product 0001
BEGIN;
--
-- Create model Product
--
CREATE TABLE "product_product" ("id" serial NOT NULL PRIMARY KEY, "name" varchar(100) NOT NULL, "category_id" integer NOT NULL);
ALTER TABLE "product_product" ADD CONSTRAINT "product_product_category_id_0c725779_fk_catalog_category_id" FOREIGN KEY ("category_id") REFERENCES "catalog_category" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "product_product_name_04ac86ce" ON "product_product" ("name");
CREATE INDEX "product_product_name_04ac86ce_like" ON "product_product" ("name" varchar_pattern_ops);
CREATE INDEX "product_product_category_id_0c725779" ON "product_product" ("category_id");
COMMIT;
Имя таблицы, созданной Django для модели продукта в приложении продукта, - product_product.
Переименовать старую таблицу
Теперь, когда у вас есть имя Django, сгенерированное для модели, вы готовы переименовать старую таблицу. Чтобы удалить модель продукта из приложения каталога, Django создал две миграции:
1. catalog / migrations / 0002_remove_product_category удаляет внешний ключ из таблицы.
2. catalog / migrations / 0003_delete_product удаляет модель.
Прежде чем вы сможете переименовать таблицу, вы хотите запретить Django отбрасывать внешний ключ в Category:
--- a/store/catalog/migrations/0002_remove_product_category.py
+++ b/store/catalog/migrations/0002_remove_product_category.py
@@ -10,8 +10,13 @@ class Migration(migrations.Migration):
]
operations = [
- migrations.RemoveField(
- model_name='product',
- name='category',
+ migrations.SeparateDatabaseAndState(
+ state_operations=[
+ migrations.RemoveField(
+ model_name='product',
+ name='category',
+ ),
+ ],
+ database_operations=[],
),
]
Использование SeparateDatabaseAndState с параметром database_operations, установленным в пустой список, предотвращает удаление столбца Django.
Django предоставляет специальную операцию миграцииAlterModelTable для переименования таблицы для модели. Отредактируйте миграцию, которая удаляет старую таблицу, и вместо этого переименуйте таблицу в product_product:
--- a/store/catalog/migrations/0003_delete_product.py
+++ b/store/catalog/migrations/0003_delete_product.py
@@ -11,7 +11,17 @@ class Migration(migrations.Migration):
]
operations = [
- migrations.DeleteModel(
- name='Product',
- ),
+ migrations.SeparateDatabaseAndState(
+ state_operations=[
+ migrations.DeleteModel(
+ name='Product',
+ ),
+ ],
+ database_operations=[
+ migrations.AlterModelTable(
+ name='Product',
+ table='product_product',
+ ),
+ ],
+ )
]
Вы использовали SeparateDatabaseAndState вместе с AlterModelTable, чтобы предоставить Django другое действие миграции для выполнения в базе данных.
Затем вам нужно запретить Django создавать таблицу для новой модели продукта. Вместо этого вы хотите, чтобы он использовал переименованную вами таблицу. Внесите следующие изменения в первоначальную миграцию в приложении продукта:
--- a/store/product/migrations/0001_initial.py
+++ b/store/product/migrations/0001_initial.py
@@ -13,12 +13,18 @@ class Migration(migrations.Migration):
]
operations = [
- migrations.CreateModel(
- name='Product',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(db_index=True, max_length=100)),
- ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='catalog.Category')),
- ],
+ migrations.SeparateDatabaseAndState(
+ state_operations=[
+ migrations.CreateModel(
+ name='Product',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(db_index=True, max_length=100)),
+ ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='catalog.Category')),
+ ],
+ ),
+ ],
+ # Table already exists. See catalog/migrations/0003_delete_product.py
+ database_operations=[],
),
]
Миграция создает модель в состоянии Django, но не создает таблицу в базе данных из-за строки database_operations = []. Помните, как вы переименовали старую таблицу в product_product? Переименовывая старую таблицу в имя, которое Django сгенерировал бы для новой модели, вы заставляете Django использовать старую таблицу.
Наконец, вы хотите, чтобы Django не воссоздавал ограничение внешнего ключа в модели Sale:
--- a/store/sale/migrations/0002_auto_20200110_1304.py
+++ b/store/sale/migrations/0002_auto_20200110_1304.py
@@ -12,9 +12,15 @@ class Migration(migrations.Migration):
]
operations = [
- migrations.AlterField(
- model_name='sale',
- name='product',
- field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='product.Product'),
- ),
+ migrations.SeparateDatabaseAndState(
+ state_operations=[
+ migrations.AlterField(
+ model_name='sale',
+ name='product',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='product.Product'),
+ ),
+ ],
+ # You're reusing an existing table, so do nothing
+ database_operations=[],
+ )
]
Теперь вы готовы к переносу:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, catalog, contenttypes, product, sale, sessions
Running migrations:
Applying catalog.0002_remove_product_category... OK
Applying product.0001_initial... OK
Applying sale.0002_auto_20200110_1304... OK
Applying catalog.0003_delete_product... OK
Отлично! Перенос прошел успешно. Но прежде чем двигаться дальше, убедитесь, что это можно изменить:
$ python manage.py migrate catalog 0001
Operations to perform:
Target specific migration: 0001_initial, from catalog
Running migrations:
Rendering model states... DONE
Unapplying catalog.0003_delete_product... OK
Unapplying sale.0002_auto_20200110_1304... OK
Unapplying product.0001_initial... OK
Unapplying catalog.0002_remove_product_category... OK
Удивительный! Миграция полностью обратима.
Примечание. AlterModelTable обычно предпочтительнее RunSQL по нескольким причинам.
Во-первых, AlterModelTable может обрабатывать отношения «многие ко многим» между полями, имена которых основаны на имени модели. Использование RunSQL для переименования таблиц может потребовать дополнительной работы.
Кроме того, встроенные операции миграции, такие как AlterModelTable, не зависят от базы данных, а RunSQL - нет. Если, например, ваше приложение должно работать с несколькими механизмами баз данных, вы можете столкнуться с некоторыми проблемами при написании SQL, совместимого со всеми механизмами баз данных.
Бонус: понимание самоанализа
Django ORM - это уровень абстракции, который переводит типы Python в таблицы базы данных и наоборот. Например, когда вы создали модель Product в приложении продукта, Django создал таблицу с именем product_product. Помимо таблиц ORM создает другие объекты базы данных, такие как индексы, ограничения, последовательности и т. Д. В Django есть соглашение об именах для всех этих объектов, основанное на именах приложения и модели.
Чтобы лучше понять, как это выглядит, просмотрите таблицу catalog_category в базе данных:
django_migration_test=# \d catalog_category
Table "public.catalog_category"
Column | Type | Nullable | Default
--------+------------------------+----------+----------------------------------------------
id | integer | not null | nextval('catalog_category_id_seq'::regclass)
name | character varying(100) | not null |
Indexes:
"catalog_category_pkey" PRIMARY KEY, btree (id)
Таблица была создана Django для модели категорий в каталоге приложений, отсюда и название catalog_category. Вы также можете заметить похожее соглашение об именах для других объектов базы данных.
1. catalog_category_pkey относится к индексу первичного ключа.
2. catalog_category_id_seq относится к последовательности для генерации значений для идентификатора поля первичного ключа.
Затем просмотрите таблицу модели продукта, которую вы переместили из каталога в продукт:
django_migration_test=# \d product_product
Table "public.product_product"
Column | Type | Nullable | Default
-------------+------------------------+----------+---------------------------------------------
id | integer | not null | nextval('catalog_product_id_seq'::regclass)
name | character varying(100) | not null |
category_id | integer | not null |
Indexes:
"catalog_product_pkey" PRIMARY KEY, btree (id)
"catalog_product_category_id_35bf920b" btree (category_id)
"catalog_product_name_924af5bc" btree (name)
"catalog_product_name_924af5bc_like" btree (name varchar_pattern_ops)
Foreign-key constraints:
"catalog_product_category_id_35bf920b_fk_catalog_category_id"
FOREIGN KEY (category_id)
REFERENCES catalog_category(id)
DEFERRABLE INITIALLY DEFERRED
На первый взгляд связанных объектов больше. Однако более пристальный взгляд показывает, что имена связанных объектов не соответствуют имени таблицы. Например, имя таблицы - product_product, но имя ограничения первичного ключа - catalog_product_pkey. Вы скопировали модель из приложения с именем catalog, поэтому это должно означать, что операция миграции AlterModelTable не изменяет имена всех связанных объектов базы данных.
Чтобы лучше понять, как работает AlterModelTable, проверьте SQL, сгенерированный этой операцией миграции:
$ python manage.py sqlmigrate catalog 0003
BEGIN;
--
-- Custom state/database change combination
--
ALTER TABLE "catalog_product" RENAME TO "product_product";
COMMIT;
Это показывает, что AlterModelTable только переименовывает таблицы. Если это так, то что произойдет, если вы попытаетесь внести изменения в один из объектов базы данных, связанных с таблицами этих объектов? Сможет ли Django обработать эти изменения?
Чтобы выяснить это, попробуйте удалить индекс на имени поля в модели Product:
--- a/store/product/models.py
+++ b/store/product/models.py
@@ -3,5 +3,5 @@ from django.db import models
from catalog.models import Category
class Product(models.Model):
- name = models.CharField(max_length=100, db_index=True)
+ name = models.CharField(max_length=100, db_index=False)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
Затем сгенерируйте миграцию:
$ python manage.py makemigrations
Migrations for 'product':
product/migrations/0002_auto_20200110_1426.py
- Alter field name on product
Команда выполнена - хороший знак. Теперь проверьте сгенерированный SQL:
$ python manage.py sqlmigrate product 0002
BEGIN;
--
-- Alter field name on product
--
DROP INDEX IF EXISTS "catalog_product_name_924af5bc";
DROP INDEX IF EXISTS "catalog_product_name_924af5bc_like";
COMMIT;
Резюме: плюсы и минусы переименования таблицы
Переименование таблицы имеет свои достоинства и недостатки. Вот некоторые из плюсов, связанных с этим подходом:
Это быстро: при таком подходе переименовываются только объекты базы данных, поэтому он выполняется очень быстро.
Это не требует простоя: при таком подходе объекты базы данных блокируются только на короткое время, пока они переименовываются, поэтому его можно выполнять в действующей системе без простоев.
Это обратимо: при необходимости можно отменить миграцию.
Он поддерживается ORM: выполнение этого перехода с использованием встроенных операций миграции гарантирует надлежащую поддержку базы данных.
Единственный потенциальный недостаток, связанный с этим подходом, заключается в том, что он нарушает соглашение об именах. Переименование только таблиц означает, что имена других объектов базы данных будут несовместимы с соглашением об именах Django. Это может вызвать некоторую путаницу при работе напрямую с базой данных. Однако Django все еще может использовать интроспекцию для идентификации этих объектов и управления ими, так что это не является серьезной проблемой.
Рекомендации: выберите лучший подход
В этом руководстве вы узнали, как перенести модель Django из одного приложения в другое тремя разными способами. Вот сравнение подходов, описанных в этом руководстве:
Metric Copy Data Change Table Rename Table
Fast ✗ ✔️ ✔️
No downtime ✗ ✔️ ✔️
Sync related objects ✗ ✔️ ✔️
Preserve naming convention. ✔️ ✗ ✔️
Built-in ORM support ✔️ ✔️ ✔️
Reversible ✔️ ✔️ ✔️
Примечание. В приведенной выше таблице предполагается, что при переименовании таблицы сохраняется соглашение об именах Django. Хотя это не совсем так, вы узнали ранее, что Django может использовать интроспекцию для преодоления проблем с именованием, связанных с этим подходом.
У каждого из вышеперечисленных подходов есть свои преимущества и недостатки. Итак, какой подход вам следует использовать?
Как правило, вы должны копировать данные, когда работаете с небольшой таблицей, и вы можете позволить себе некоторое время простоя. В противном случае лучше всего переименовать таблицу и привязать к ней новую модель.
При этом у каждого проекта есть свои уникальные требования. Вам следует выбрать тот подход, который больше всего подходит вам и вашей команде.
Заключение
Прочитав это руководство, вы сможете принять правильное решение о том, как перенести модель Django в другое приложение, в зависимости от вашего конкретного варианта использования, ограничений и требований.
Из этого урока вы узнали:
Как перенести модель Django из одного приложения в другое
Как использовать расширенные функции интерфейса командной строки миграции Django, такие как sqlmigrate, showmigrations и sqlsequencereset
Как составить и проверить план миграции
Как сделать миграцию обратимой и как отменить миграцию
Что такое интроспекция и как Django использует ее при миграции
Чтобы погрузиться глубже, ознакомьтесь с полным набором руководствпо базам данных ируководств по Django..