Хотелось бы поделиться одним из возможных вариантов перемещения модели данных из одного приложения в другое.
Сразу отмечу, что данный вариант переноса модели данных не является стопроцентно рабочим и может понадобиться дополнительное ручное редактирование таблиц для правильной установки Content Type. Поскольку любые подобные модификации чреваты потерей данных для GenericForeignKey отношений.
В моём случае GenericForeignKey не использовались, поэтому такой проблемы не возникло.
Исходные данные
Допустим, у вас имеется старое приложение article, в котором есть модель Article. Вам нужно создать приложение blog и перенести модель Article в данное приложение. Используется база данных PostgreSQL.
Перемещение модели
Сделать резервную копию базы данных (backup), можете сделать дамп базы данных и потренироваться на данном дампе на development сервере.
Перенести код модели Article из article.models.py в blog.models.py . То есть код из старой модели необходимо удалить.
Отредактировать класс Meta в модели Article , чтобы сохранить старое название таблицы. В данном случае оно было article_artilce
class Meta: db_table = 'article_article'
Изменить все ссылки на модели данных во всём проекте. Это означаете поменять все импорты и ForeignKey .
Создать миграции для обоих приложений в следующем порядке
python manage.py makemigrations blog python manage.py makemigrations article
Применить миграции в режиме фейка. Это позволит добавить миграции как применённые, но при этом никаких изменений производиться не будет.
python manage.py migrate --fake
В данный момент база данных будет уязвима в том плане, что не был создан Content Type для вашей перенесённой модели данных.Исправить это можно будет следующими действиями. Нужно теперь поменять имя таблицы, а сделать это можно будет удалением db_table из модели данных и созданием и применением новой миграции к новому приложению. Content Type будет создан автоматически при применении миграции.
python manage.py makemigrations blog python manage.py migrate
По сути после этих действий база данных будет рабочей, а данные будут сохранены. Но могут понадобиться дополнительные действия для корректировки базы данных.
Чистка базы данных и корректировка
В зависимости от того, где и как использовалась перенесённая модель данных. Могут понадобиться те или иные действия. Например корректировка Content Type для GenericForeignKey .
У меня был наиболее простой случай - это удаление старых Content Type . Для этого нужно подключиться к базе данных и удалить информацию из всех таблиц, которые могут ссылаться на Content Type старой модели.
Такими таблицами могут быть:
- auth_user_user_permissions
- auth_permission
- django_content_type
- и т.д. остальные модели, в которых могли использоваться старые модели до переноса
Я покажу на примере старых Content Type из своей базы данных.
Процесс чистки
-
Подключимся к базе данных
sudo -u postgres psql \c myprojectdb
Посмотрим таблицу django_content_type, чтобы понять, какие id имеют устаревшие Content Type.
SELECT * FROM django_content_type;
Вывод таблицыid | app_label | model ----+------------------+----------------------- 1 | accounts | userprofile 2 | knowledge | section 3 | knowledge | article 4 | admin | logentry 5 | auth | permission 6 | auth | group 7 | auth | user 8 | contenttypes | contenttype 9 | sessions | session
Найдя старые id можем попытаться их удалить (Например 50 и 51)
DELETE FROM django_content_type WHERE id BETWEEN 50 AND 51;
Если не получилось, то нужно найти, где используются данные ID. В ошибке будет указано, в каких таблицах они задействованы. В моём случае это были auth_permission и auth_user_user_permissions.
SELECT * FROM auth_permission WHERE content_type_id BETWEEN 50 AND 51;
Вывод таблицыid | name | content_type_id | codename -----+--------------------+-----------------+---------------- 154 | Can add chat | 50 | add_chat 155 | Can change chat | 50 | change_chat 156 | Can delete chat | 50 | delete_chat 157 | Can add message | 51 | add_message 158 | Can change message | 51 | change_message 159 | Can delete message | 51 | delete_message 276 | Can view Chat | 50 | view_chat 277 | Can view Message | 51 | view_message
Далее ищем интересующие id в auth_user_user_permissionsSELECT * FROM auth_user_user_permissions WHERE permission_id=154;
Выводid | user_id | permission_id -----+---------+--------------- 102 | 2 | 154
Удаляем из таблицы auth_user_user_permissions все permission для старого Content Type.
DELETE FROM auth_user_user_permissions WHERE permission_id=154;
Удаляем из таблицы auth_permission сами permission.
DELETE FROM auth_permission WHERE content_type_id BETWEEN 50 AND 51;
Удаляем Content Types.
DELETE FROM django_content_type WHERE id BETWEEN 50 AND 51;
Заключение
Таким образом можно переместить интересующую модель данных из одного приложения в другое, а также можно подчистить базу данных от устаревшего контента. Однако необходимо очень осторожно работать с базой данных, чтобы не нарушить её целостность.
Для Django рекомендую VDS-сервера хостера Timeweb .
Дружище, ну подскажи. Совершенно не понял 7-й пункт. А именно, а что происходит с данными, которые в таблице? Или всё делалось, когда в таблице нет данных?
Дело в том, что у меня как раз похожая ситуация. Только нужно перенести три модели, в другое приложение. Понимаю, что всё начинается с одной. Но вот незадача. Все эти таблицы уже заполнены. И связь с этими данными уже достаточно прочная (по факту нужно перенесети каталог, из данных которого уже полно действующих заказов).
И вот мне не ясен такой момент. Вот была article_article. Я из META убираю строчку. Делаю makemigrations и migrate. Так и что происходит с таблицей? Она становиться новенькой, или просто переименовывается?
Я понимаю, что бэкапы и всё такое, но как-то честно сказать очково. Буду потом плясать с этим бэкапом.
Всё делалось, когда данные были в таблице. Сам очковал по полной, когда впервые так делал, раз на десять издевался над дампом на дев сервере, чтобы ничего не поломать.
Суть в том, что старая модель данных переносится в другое приложение, но с помощью db_table = 'article_article' старая таблица сохраняется.
Когда удаляется article_article то в миграции создаётся новое имя таблицы. То есть старая таблица переименовывается, а данные в ней сохраняются.
При этом в шаге 3 таблица условно была удалена из старого приложения, за счёт fake миграции. То есть информация о миграции добавилась, но при этом изменений не произошло.
А вот шаг 7 как раз переименовывает таблицу, чтобы она фактически лежала в новом приложении. Так что ДА - она переименовывается с сохранением данных.
Ну я на свой страх и риск, ночью всё попробовал локально, а потом уже удалил таблицы базы на сервере и залил обновлённый дамп. Спасибо большое. Инструкция реально помогла. Правда пришлось поплясать с сылками на разные роуты. Но оно того стоило. Порядок в папках и в базе радует глаз.
Сам долго колупал форумы, чтобы найти полноценное решение, как это сделать и ничего не поломать.
Иногда там ещё бывают циклические зависимости - вот это реально проблема. Но в итоге тоже нашёл решение через сброс всех миграций и создание одной initial миграции и применение её через fake. Подробнее в этой статье .