Django 迁移 RunPython 不能调用模型方法

我正在使用 RunPython方法创建一个数据迁移。但是,当我尝试在对象上运行方法时,没有定义任何方法。是否可以使用 RunPython调用在模型上定义的方法?

22133 次浏览

你给你的模型打电话了吗,就像文档里说的那样?

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()

数据迁移 因为此时,您不能直接导入 Model:

from yourappname.models import Person

更新

内部 Django 代码位于这个文件 Django/db/mobilations/state.py 中 迁移. 状态. ModelState # struct _ 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

模型方法在迁移中不可用,包括数据迁移。

但是有一个变通方法,它应该非常类似于调用模型方法。您可以在迁移中定义模拟您想要使用的那些模型方法的函数。

如果你有这种方法:

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)
]

这样迁移才能成功。代码会有一些重复,但这并不重要,因为在应用程序的特定状态下,数据迁移应该是一次性操作。

从 Django 1.8开始,您可以通过在模型管理器上设置 use_in_migrations = True来使模型管理器可用于迁移。看看 迁移文件

小字印在 历史模型

因为不可能序列化任意的 Python 代码,所以这些历史模型不会有您定义的任何自定义方法。

当我第一次在迁移过程中遇到它时,我非常惊讶,因为它似乎与它们的 设计哲学(在模型周围添加函数)相矛盾,所以我没有仔细阅读细则

这不能回答 OP,但可能仍然对某些人有用。

不仅自定义模型方法在迁移中不可用,其他模型属性也是如此,例如用于模型字段 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
...

当你有许多复杂的方法相互调用并且你需要通过你的对象使用它们的时候,有些东西对我很有用:

首先将这些模型方法复制到您的迁移文件中

def A(self):
return self.B() + self.C()


def B(self):
return self.name


def C(self):
return self.description


然后在你的迁移函数中:

def do_something_to_your_objects(apps, schema_editor):
MyModel = apps.get_model("my_app", "MyModel")
MyModel.A = A
MyModel.B = B
MyModel.C = C
    

for my_object in MyModel.objects.all():
my_object.name_and_decription = my_object.C()
my_object.save()


class Migration(migrations.Migration):


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


operations = [
migrations.RunPython(do_something_to_your_objects)
]