Миграции Django RunPython не может вызывать методы модели

Я создаю миграцию данных, используя метод RunPython. Однако, когда я пытаюсь запустить метод для объекта, ни один из них не определен. Можно ли вызвать метод, определенный в модели, с помощью RunPython?


person user2954587    schedule 28.02.2015    source источник


Ответы (5)


Методы модели недоступны в миграциях, включая миграции данных.

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

Если у вас был этот метод:

class Order(models.Model):
    '''
    order model def goes here
    '''

    def get_foo_as_bar(self):
        new_attr = 'bar: %s' % self.foo
        return new_attr

Вы можете написать функцию внутри скрипта миграции, например:

def get_foo_as_bar(obj):
    new_attr = 'bar: %s' % obj.foo
    return new_attr


def save_foo_as_bar(apps, schema_editor):
    old_model = apps.get_model("order", "Order")

    for obj in old_model.objects.all():
        obj.new_bar_field = get_foo_as_bar(obj)
        obj.save()

Затем используйте его в миграции:

class Migration(migrations.Migration):

    dependencies = [
        ('order', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(save_foo_as_bar)
    ]

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

person chhantyal    schedule 07.06.2016
comment
Вы могли бы просто сказать: скопируйте + вставьте свои методы. Это очевидно... но не решает проблему. Что делать, если ваш метод более сложный? Что, если вы импортируете функцию, которая вызывает этот метод...? - person Andrey St; 09.09.2019
comment
@AndreySt на самом деле имеет смысл. код вашего приложения может меняться с течением времени... но если код, используемый в какой-либо данной миграции, изменится, последующие миграции могут завершиться неудачей, оставив вас в несогласованном состоянии, если вам нужно перестроить базу данных с нуля. - person connorbode; 12.04.2020
comment
Официальная информация Django doc, связанная с этим: docs.djangoproject.com/en /3.1/темы/миграции/ - person Quitiweb; 22.09.2020

Вы назвали свою модель, как сказано в документации?

def combine_names(apps, schema_editor):
    # We can't import the Person model directly as it may be a newer
    # version than this migration expects. We use the historical version.
    Person = apps.get_model("yourappname", "Person")
    for person in Person.objects.all():
        person.name = "%s %s" % (person.first_name, person.last_name)
        person.save()

Перенос данных на данный момент вы не можете напрямую импортировать свою модель:

from yourappname.models import Person

Обновить

Внутренний код Django находится в этом файле django/db/migrations/state.py django.db.migrations.state.ModelState#construct_fields.

def construct_fields(self):
    "Deep-clone the fields using deconstruction"
    for name, field in self.fields:
        _, path, args, kwargs = field.deconstruct()
        field_class = import_string(path)
        yield name, field_class(*args, **kwargs)

Есть только поля, которые являются клонами в «поддельном» экземпляре модели:

MyModel.__module__ = '__fake__'

Github Django

person Azman0101    schedule 28.02.2015
comment
да. вы можете получить доступ к полям модели, но не к методам моделей - person user2954587; 28.02.2015
comment
Проголосовал за полезный комментарий в combine_names. Я совершенно забыл, что вы не можете импортировать модель напрямую - много ломал голову, пытаясь заставить миграции работать, прежде чем мне напомнили об этом. - person Jihoon Baek; 18.02.2019

Мелкий шрифт указан в Исторические модели.

Поскольку невозможно сериализовать произвольный код Python, эти исторические модели не будут иметь никаких пользовательских методов, которые вы определили.

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

person user2829759    schedule 08.04.2019

Начиная с Django 1.8, вы можете сделать менеджеры моделей доступными для миграции, установив use_in_migrations = True в менеджере моделей. См. документацию по миграции.

person Ryan Knight    schedule 22.08.2018
comment
это сработало для меня! Но не забудьте запустить миграцию, чтобы обновить измененный менеджер — и сделать это зависимостью от переноса данных, который вы пишете. - person erikvw; 02.10.2018

Это не отвечает на ОП, но может кому-то пригодиться.

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

В этом конкретном пограничном случае мы не можем получить доступ к историческому. значения вариантов напрямую во время миграции, но мы можем получить исторические значения из поля модели, используя model _meta API, поскольку эти значения содержатся в миграциях.

Учитывая Student пример:

class Student(models.Model):
    FRESHMAN = 'FR'
    ...
    YEAR_IN_SCHOOL_CHOICES = [(FRESHMAN, 'Freshman'), ...]
    year_in_school = models.CharField(
        max_length=2,
        choices=YEAR_IN_SCHOOL_CHOICES,
        default=FRESHMAN,
    )

Мы можем получить историческое значение Student.FRESHMAN внутри миграции следующим образом:

...
Student = apps.get_model('my_app', 'Student')
YEAR_IN_SCHOOL_CHOICES = Student._meta.get_field('year_in_school').choices
...
person djvg    schedule 01.03.2021