Evgenii Legotckoi
Evgenii Legotckoi24 апреля 2019 г. 3:42

Django - Урок 045. Перемещение моделей из одного приложения в другое

Я хотел бы поделиться одним из возможных способов переноса модели данных из одного приложения в другое.

Сразу отмечу, что этот вариант переноса модели данных не на 100% рабочий и может потребоваться дополнительная ручная правка таблиц для корректной установки Content Type. Так как любые подобные модификации чреваты потерей данных для отношений GenericForeignKey.

В моем случае GenericForeignKey не использовался, поэтому такой проблемы не было.


Исходные данные

Предположим, у вас есть старая статья article с моделью Article. Вам нужно создать приложение для блога и перенести модель статьи в это приложение. Использует базу данных PostgreSQL.

Перемещение модели

  1. Сделайте резервную копию базы (backup), можете сделать дамп базы и потренироваться на этом дампе на сервере разработки.

  2. Переместите код модели Article из article.models.py в blog.models.py . То есть код от старой модели надо убрать.

  3. Измените класс Meta в модели Article , чтобы сохранить старое имя таблицы. В данном случае это была article_artilce

    class Meta:
          db_table = 'article_article'

  4. Изменить все ссылки на модели данных во всем проекте. Это означает изменение всего импорта и ForeignKey .

  5. Создайте миграции для обоих приложений в следующем порядке.

    python manage.py makemigrations blog
      python manage.py makemigrations article

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

    python manage.py migrate --fake

    На данный момент база данных будет уязвима в том смысле, что она не была создана Тип контента для вашей переданной модели данных.

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

    python manage.py makemigrations blog
      python manage.py migrate

По сути, после этих действий база данных будет работать, а данные будут сохранены. Но вам могут потребоваться дополнительные шаги для настройки базы данных.

Очистка и корректировка базы данных

В зависимости от того, где и как использовалась переданная модель данных. Вам могут понадобиться некоторые действия. Например, корректировка Тип контента для GenericForeignKey .

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

Такие таблицы могут быть:

  • auth_user_user_permissions
  • auth_permission
  • django_content_type
  • и т.д. другие модели, в которых можно было использовать старые модели до передачи

Я покажу пример старого Content Type из моей базы данных.

Процесс очистки

  1. Подключиться к базе данных

    sudo -u postgres psql
      \c myprojectdb

  2. Давайте посмотрим на таблицу django_content_type, чтобы увидеть, какие идентификаторы имеют устаревший тип контента.

    SELECT * FROM django_content_type;

    Table output
    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

  3. Найдя старые id мы можем попробовать их удалить (например 50 и 51)

    DELETE FROM django_content_type WHERE id BETWEEN 50 AND 51;

  4. Если не получилось, то нужно найти, где используются данные 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_permissions
    SELECT * FROM auth_user_user_permissions WHERE permission_id=154;

    Вывод таблицы
    id  | user_id | permission_id
      -----+---------+---------------
      102 |       2 |           154

  5. Убираем из таблицы auth_user_user_permissions все разрешения для старого Content Type.

    DELETE FROM auth_user_user_permissions WHERE permission_id=154;

  6. Убираем разрешение из таблицы auth_permission.

    DELETE FROM auth_permission WHERE content_type_id BETWEEN 50 AND 51;

  7. Удалить типы контента.

    DELETE FROM django_content_type WHERE id BETWEEN 50 AND 51;

Вывод

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

For Django, I recommend Timeweb VDS-server .

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

Макар Карабасов
  • 1 июля 2019 г. 17:45

Дружище, ну подскажи. Совершенно не понял 7-й пункт. А именно, а что происходит с данными, которые в таблице? Или всё делалось, когда в таблице нет данных?

Дело в том, что у меня как раз похожая ситуация. Только нужно перенести три модели, в другое приложение. Понимаю, что всё начинается с одной. Но вот незадача. Все эти таблицы уже заполнены. И связь с этими данными уже достаточно прочная (по факту нужно перенесети каталог, из данных которого уже полно действующих заказов).

И вот мне не ясен такой момент. Вот была article_article. Я из META убираю строчку. Делаю makemigrations и migrate. Так и что происходит с таблицей? Она становиться новенькой, или просто переименовывается?

Я понимаю, что бэкапы и всё такое, но как-то честно сказать очково. Буду потом плясать с этим бэкапом.

Всё делалось, когда данные были в таблице. Сам очковал по полной, когда впервые так делал, раз на десять издевался над дампом на дев сервере, чтобы ничего не поломать.

Суть в том, что старая модель данных переносится в другое приложение, но с помощью db_table = 'article_article' старая таблица сохраняется.

Когда удаляется article_article то в миграции создаётся новое имя таблицы. То есть старая таблица переименовывается, а данные в ней сохраняются.

При этом в шаге 3 таблица условно была удалена из старого приложения, за счёт fake миграции. То есть информация о миграции добавилась, но при этом изменений не произошло.

А вот шаг 7 как раз переименовывает таблицу, чтобы она фактически лежала в новом приложении. Так что ДА - она переименовывается с сохранением данных.

Макар Карабасов
  • 2 июля 2019 г. 4:56

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

Сам долго колупал форумы, чтобы найти полноценное решение, как это сделать и ничего не поломать.

Иногда там ещё бывают циклические зависимости - вот это реально проблема. Но в итоге тоже нашёл решение через сброс всех миграций и создание одной initial миграции и применение её через fake. Подробнее в этой статье .

bernar92
  • 9 ноября 2021 г. 0:46
  • (ред.)

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

operations = [
        migrations.RunSQL('''
            ALTER TABLE IF EXISTS credits_operatorcreditapplication RENAME TO operator_creditapplication;
            create table credits_operatorcreditapplication();

            delete from auth_group_permissions
            USING (select *
                   from auth_permission
                   join django_content_type dct on dct.id = auth_permission.content_type_id
            ) dct
            where dct.app_label='credits' and dct.model='operatorcreditapplication';

            DELETE FROM auth_permission
            USING django_content_type
            where django_content_type.app_label='credits' and django_content_type.model='operatorcreditapplication';

            DELETE FROM django_admin_log
            USING django_content_type
            where django_content_type.app_label='credits' and django_content_type.model='operatorcreditapplication';

            DELETE FROM django_content_type WHERE app_label='credits' and model='operatorcreditapplication';
        '''),
        migrations.DeleteModel(
            name='OperatorCreditApplication',
        ),
    ]



operations = [
        migrations.CreateModel(
            name='OperatorCreditApplication',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('created_at', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Время создания')),
                ('changed_at', models.DateTimeField(auto_now=True, db_index=True, verbose_name='Время последнего изменения'))
            ],
            options={
                'verbose_name': '....',
                'verbose_name_plural': '...',
                'ordering': ('created_at'),
            },
        ),
        migrations.RunSQL('''
            DROP TABLE IF EXISTS scoring_operatorcreditapplication;
            ALTER TABLE IF EXISTS operator_creditapplication RENAME TO scoring_operatorcreditapplication;
        ''')

    ]

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
ОН

C++ - Тест 006. Перечисления

  • Результат:10баллов,
  • Очки рейтинга-10
K
  • KiRi4
  • 7 сентября 2023 г. 17:57

C++ - Тест 002. Константы

  • Результат:41баллов,
  • Очки рейтинга-8
K
  • KiRi4
  • 7 сентября 2023 г. 17:49

C++ - Тест 001. Первая программа и типы данных

  • Результат:66баллов,
  • Очки рейтинга-1
Последние комментарии
IscanderChe
IscanderChe13 сентября 2023 г. 19:11
Пример использования QScintilla C++ По горячим следам (с другого форума вопрос задали, пришлось в памяти освежить всё) решил дополнить. Качаем исходники с https://riverbankcomputing.com/software/qscintilla/downlo…
Evgenii Legotckoi
Evgenii Legotckoi6 сентября 2023 г. 17:18
Qt/C++ - Урок 048. QThread - работа с потоками с помощью moveToThread Разве могут взаимодействовать объекты из разных нитей как-то, кроме как через сигнал-слоты?" Могут. Выполняя оператор new , Вы выделяете под объект память в куче (heap), …
AC
Andrei Cherniaev5 сентября 2023 г. 13:37
Qt/C++ - Урок 048. QThread - работа с потоками с помощью moveToThread Я поясню свой вопрос. Выше я писал "Почему же в методе MainWindow::on_write_1_clicked() Можно обращаться к методам exampleObject_1? Разве могут взаимодействовать объекты из разных…
n
nvn31 августа 2023 г. 19:47
QML - Урок 004. Сигналы и слоты в Qt QML Здравствуйте! Прекрасный сайт, отличные статьи. Не хватает только готовых проектов для скачивания. Многих комментариев типа appCore != AppCore просто бы не было )))
NSProject
NSProject24 августа 2023 г. 23:40
Django - Урок 023. Like Dislike система с помощью GenericForeignKey Ваша ошибка связана с gettext from django.utils.translation import gettext_lazy as _ Поле должно выглядеть так vote = models.SmallIntegerField(verbose_name=_("Голос"), choices=VOTES) …
Сейчас обсуждают на форуме
IscanderChe
IscanderChe17 сентября 2023 г. 19:24
Интернационализация строк в QMessageBox Странная картина... Сделал минимально работающий пример - всё работает. Попробую на другой операционке. Может, дело в этом.
NSProject
NSProject17 сентября 2023 г. 18:49
Помогите добавить Ajax в проект В принципе ничего сложного с отправкой на сервер нет. Всё что ты хочешь отобразить на странице передаётся в шаблон и рендерится. Ты просто создаёшь файл forms.py в нём описываешь свою форму и в …
BlinCT
BlinCT15 сентября 2023 г. 22:35
Размеры полей в TreeView Всем привет. Пытаюсь сделать дерево вот такого вида Пытаюсь организовать делегат для каждой строки в дереве. ТО есть отступ какого то размера и если при открытии есть под…
IscanderChe
IscanderChe8 сентября 2023 г. 22:07
Кастомная QAbstractListModel и цвет фона, цвет текста и шрифт Похоже надо не абстрактный , а "реальный" типа QSqlTableModel Да, но не совсем. Решилось с помощью стайлшитов и setFont. Спасибо за отлик!
Evgenii Legotckoi
Evgenii Legotckoi6 сентября 2023 г. 16:35
Вопрос: Нужно ли в деструкторе удалять динамически созданные QT-объекты. Напр: Зависит от того, как эти объекты были созданы. Если вы передаёте указатель на parent объект, то не нужно, Ядро Qt само разрулит удаление, если нет, то нужно удалять вручную, иначе будет ут…

Следите за нами в социальных сетях