Миграции Django — получение текущего имени приложения в функции RunPython

#django #django-migrations

#django #django-миграции

Вопрос:

В одной из моих миграций есть дополнительная функция, которую нужно запускать с помощью RunPython . Цель этого сообщения — только показать пользователю радостное сообщение. Единственная проблема в том, что мне нужно показать имя приложения, в котором была создана и запущена текущая миграция. Возможно ли это в некоторой степени?

Мой код:

 from django.db import migrations, models
import django.db.models.deletion


def happy_message(apps, schema_editor):
    print('A happy message from migration inside app named {}')


class Migration(migrations.Migration):

    operations = [
        migrations.AddField(
            model_name='mymodel',
            name='rank_no',
            field=models.IntegerField(null=True),
        ),
        migrations.RunPython(
            happy_message,
            migrations.RunPython.noop
        ),
    ]
  

Комментарии:

1. Вы можете назвать свою миграцию, используя опцию флага —name

Ответ №1:

Вы можете вручную исправить code reverse_code ) атрибут вашего пользовательского RunPython для поддержки дополнительного аргумента app_label :

 from django.db import migrations


class AppAwareRunPython(migrations.RunPython):

    # MonkeyPatch the `code` call with a lambda that add an extra argument `app_label`
    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        mp_code = self.code
        self.code = lambda apps, se: mp_code(apps, se, app_label)
        super().database_forwards(app_label, schema_editor, from_state, to_state)

    # Same for backwards if you want to display message when unapplying the migration
    def database_backwards(self, app_label, schema_editor, from_state, to_state):
        if self.reverse_code:
            mp_reverse_code = self.reverse_code
            self.reverse_code = lambda apps, se: mp_reverse_code(apps, se, app_label)
        super().database_backwards(app_label, schema_editor, from_state, to_state)

    # Add the etra argument to noop
    @staticmethod
    def noop(apps, schema_editor, app_label=None):
        migrations.RunPython.noop(apps, schema_editor)


# This function can't be used with classic RunPython unless you put `app_label=None` and test its value
def happy_message(apps, schema_editor, app_label):
    print(f'A happy message from migration inside app named {app_label}')


class Migration(migrations.Migration):

    operations = [
        AppAwareRunPython(happy_message, AppAwareRunPython.noop),
    ]
  

Ответ №2:

Если вы используете классическую файловую архитектуру Django, то ваш файл миграции должен быть расположен в project_dir/app_dir/migrations/0001_migration_file.py .

Затем вы можете просто получить имя каталога приложения:

 from os.path import basename, dirname

def happy_message(apps, schema_editor):
    app_name = basename(dirname(dirname(__file__)))
    print(f'A happy message from migration inside app named {app_name}')
  

Комментарии:

1. Это сработало бы. Однако это кажется немного странным.

2. Да, это так, но я еще не нашел способа восстановить атрибут «app_label» из объекта миграции из RunPython ^^

3. Я не горжусь, но вот эквивалентный код, использующий новый модуль pathlib , доступный с Python 3.4 и выше: app_name = Path(__file__).parents[1].name