Django

Как переименовать приложение в Django

Aidas Bendoraitis, автор книги «Django 3 Web Development Cookbook», рассказал, как переименовать приложение в Django.


Когда я начал работу над MVP (минимально жизнеспособным продуктом) моего проекта 1st things 1st, я сперва думал, что этот Django-проект будет посвящен приоритизации. Несколько лет спустя я понял, что этот проект по сути SaaS (software as a service, ПО как услуга), а приоритизация — лишь часть необходимого функционала для SaaS. В конечном итоге мне понадобилось переименовать приложение, чтобы получить более чистый и лучше организованный код. Вот как я это сделал.

0. Обновите ваш код и кодовую базу

Убедитесь, что вы сделали git pull последних изменений и выполнили все миграции базы данных.

[python_ad_block]

1. Установите django-rename-app

Поместите django-rename-app в pip requirements и установите его. Либо просто запустите следующую команду:

(venv)$ pip install django-rename-app

Внесите это приложение в INSTALLED_APPS в ваших настройках:

INSTALLED_APPS = [
    # …
    "django_rename_app",
]

2. Переименуйте директории приложения

Смените старое имя (oldapp) на новое (newapp) в ваших приложениях и шаблонах.

3. Измените имя приложения там, где оно встречается в коде

Измените имя во всех ваших импортах, миграциях и путях шаблонов.

Можно сделать глобальный поиск oldapp, а затем перебрать все результаты.

4. Запустите управляющую команду rename_app

Выполните следующую команду:

(env)$ python manage.py rename_app oldapp newapp

Эта команда заменит префикс имени в таблицах приложения и записях в таблицах django_content_type и django_migrations.

Если планируете обновить сервера стейджинга и продакшена, добавьте команду rename_app перед запуском миграций в своих скриптах деплоймента (Ansible, Docker и т.п..).

5. Обновите индексы и ограничения

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

(env)$ python manage.py makemigrations newapp --empty --name rename_indexes

Заполните миграцию следующим кодом:

# newapp/migrations/0002_rename_indexes.py
from django.db import migrations


def named_tuple_fetch_all(cursor):
    "Return all rows from a cursor as a namedtuple"
    from collections import namedtuple

    desc = cursor.description
    Result = namedtuple("Result", [col[0] for col in desc])
    return [Result(*row) for row in cursor.fetchall()]


def rename_indexes(apps, schema_editor):
    from django.db import connection

    with connection.cursor() as cursor:
        cursor.execute(
            """SELECT indexname FROM pg_indexes 
            WHERE tablename LIKE 'newapp%'"""
        )
        for result in named_tuple_fetch_all(cursor):
            old_index_name = result.indexname
            new_index_name = old_index_name.replace(
                "oldapp_", "newapp_", 1
            )
            cursor.execute(
                f"""ALTER INDEX IF EXISTS {old_index_name} 
                RENAME TO {new_index_name}"""
            )


def rename_foreignkeys(apps, schema_editor):
    from django.db import connection

    with connection.cursor() as cursor:
        cursor.execute(
            """SELECT table_name, constraint_name 
            FROM information_schema.key_column_usage
            WHERE constraint_catalog=CURRENT_CATALOG 
            AND table_name LIKE 'newapp%'
            AND position_in_unique_constraint notnull"""
        )
        for result in named_tuple_fetch_all(cursor):
            table_name = result.table_name
            old_foreignkey_name = result.constraint_name
            new_foreignkey_name = old_foreignkey_name.replace(
                "oldapp_", "newapp_", 1
            )
            cursor.execute(
                f"""ALTER TABLE {table_name} 
                RENAME CONSTRAINT {old_foreignkey_name} 
                TO {new_foreignkey_name}"""
            )


class Migration(migrations.Migration):

    dependencies = [
        ("newapp", "0001_initial"),
    ]

    operations = [
        migrations.RunPython(rename_indexes, migrations.RunPython.noop),
        migrations.RunPython(rename_foreignkeys, migrations.RunPython.noop),
    ]

Запустите миграции:

(env)$ python manage.py migrate

Если что-то не работает так, как ожидалось, мигрируйте обратно, исправьте код и затем снова мигрируйте. Отменить миграцию можно путем миграции на один шаг до последней миграции. Например:

(env)$ python manage.py migrate 0001

6. Уборка

После применения миграции во всех необходимых окружениях можно подчистить их путем удаления django-rename-app из pip requirements и скриптов деплоймента.

Итоги

Редко удается построить систему, которая изначально будет отвечать всем вашим нуждам. Системы всегда требуют постоянного улучшения и рефакторинга. Используя миграции Django и django-rename-app, можно работать над сайтами в стиле Agile, чисто и гибко.

Перевод статьи «How to Rename a Django App».

Марина

Recent Posts

10 удалённых Python-вакансий

Если вы ищете работу на Python, на Talanto.work сейчас есть варианты для junior, middle, senior…

6 месяцев ago

Где искать Python-вакансии в 2026 году: зачем на Talanto.work появился отдельный сценарий поиска

Рынок Python-вакансий большой, но удобным его назвать сложно. Формально вакансий много, однако в реальном поиске…

6 месяцев ago

Срезы в Python со всеми подробностями

Срезы в Python — это больше, чем a[2:7]. В статье — быстрый разбор полезных приёмов:…

8 месяцев ago

Возведение в степень в Python: полное руководство

В этой статье мы рассмотрим возведение в степень в Python с помощью различных методов, от…

9 месяцев ago

Как удалить дубликаты из списка Python?

Дубликаты в списке Python — частая проблема. В статье показано, как быстро убрать повторы, в…

9 месяцев ago

Виртуальный (shared) хостинг от SpaceWeb: технический обзор

Услуга виртуального хостинга на SpaceWeb позволяет арендовать часть сервера (shared-сервера) и разместить на нём свой…

11 месяцев ago