Я создаю миграцию данных, используя метод RunPython
. Однако, когда я пытаюсь запустить метод для объекта, ни один из них не определен. Можно ли вызвать метод, определенный в модели, с помощью RunPython
?
Миграции Django RunPython не может вызывать методы модели
Ответы (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)
]
Таким образом, миграции будут работать. Будет немного повторения кода, но это не имеет значения, потому что миграция данных должна быть одноразовой операцией в определенном состоянии приложения.
Вы назвали свою модель, как сказано в документации?
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__'
combine_names
. Я совершенно забыл, что вы не можете импортировать модель напрямую - много ломал голову, пытаясь заставить миграции работать, прежде чем мне напомнили об этом.
- person Jihoon Baek; 18.02.2019
Мелкий шрифт указан в Исторические модели.
Поскольку невозможно сериализовать произвольный код Python, эти исторические модели не будут иметь никаких пользовательских методов, которые вы определили.
Это было довольно неожиданно, когда я впервые столкнулся с этим во время миграции и не читал мелкий шрифт, потому что это, кажется, противоречит их Философия дизайна (добавление функций вокруг моделей)
Начиная с Django 1.8, вы можете сделать менеджеры моделей доступными для миграции, установив use_in_migrations = True
в менеджере моделей. См. документацию по миграции.
Это не отвечает на ОП, но может кому-то пригодиться.
В миграции недоступны не только пользовательские методы модели, но и другие атрибуты модели, такие как константы класса, используемые для поля модели 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
...