Django迁移RunPython无法调用模型方法

w46czmvw  于 2023-02-06  发布在  Go
关注(0)|答案(7)|浏览(120)

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

h43kikqp

h43kikqp1#

模型方法在迁移(包括数据迁移)中不可用。
然而,有一种解决方法,它应该与调用模型方法非常相似,你可以在迁移中定义函数来模拟你想要使用的那些模型方法。
如果你有这个方法:

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

迁移将以这种方式工作,虽然会有一些代码的重复,但这并不重要,因为数据迁移应该是应用程序特定状态下的一次性操作。

k97glaaz

k97glaaz2#

你有没有像文档里说的那样调用你的模型?

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/www.example.com django. db. migrations. state. ModelState #construct_fieldsstate.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__'

吉图布· Django

ufj5ltwl

ufj5ltwl3#

在历史模型中打下了小字
因为不可能序列化任意的Python代码,所以这些历史模型将没有您定义的任何定制方法。
当我在迁移过程中第一次遇到它时,我感到非常惊讶,因为它似乎与他们的设计哲学(围绕模型添加功能)相矛盾,所以我没有阅读其中的小字。

odopli94

odopli944#

从Django1.8开始,您可以通过在模型管理器上设置use_in_migrations = True来使模型管理器可用于迁移。

6xfqseft

6xfqseft5#

这并没有回答OP,但可能对某些人仍然有用。
不仅定制模型方法在迁移中不可用,其他模型属性也是如此,比如用于模型字段choices的类“常量”。
在这个特定的边缘案例中,我们无法在迁移过程中直接访问选项的历史值,但是我们 * 可以 * 使用model _meta api从model字段获取历史值,因为这些值包含在迁移中。
以Django的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
...
62lalag4

62lalag46#

当你有许多复杂的方法互相调用,并且你需要通过你的对象来使用它们时,这对我来说是很有用的:
首先将这些模型方法复制到迁移文件中

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

khbbv19g7#

如果你像我一样,因为得到错误ValueError: RunPython must be supplied with a callable而来到这里,这是因为你在函数的末尾放了"()",你在migrations.RunPython中给code赋值
错误,例如migrations.RunPython(code=do_something(), reverse=noop)
它应该是:migrations.RunPython(code=do_something, reverse=noop),不带()

相关问题