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

use*_*587 45 django django-models django-migrations

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

chh*_*yal 40

迁移中不提供模型方法,包括数据迁移.

但是有一些解决方法,它应该与调用模型方法非常相似.您可以在迁移中定义模拟要使用的模型方法的函数.

如果你有这个方法:

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

    def get_foo_as_bar(self):
        new_attr = 'bar: %s' % self.foo
        return new_attr
Run Code Online (Sandbox Code Playgroud)

您可以在迁移脚本中编写函数,如:

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()
Run Code Online (Sandbox Code Playgroud)

然后在迁移中使用它:

class Migration(migrations.Migration):

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

    operations = [
        migrations.RunPython(save_foo_as_bar)
    ]
Run Code Online (Sandbox Code Playgroud)

这样迁移就可以了.将会有一些重复的代码,但这并不重要,因为数据迁移应该是在应用程序的特定状态下的一次操作.

  • 您可以只是说:“复制+粘贴您的方法”。这是显而易见的……但并不能解决问题。如果你的方法更复杂怎么办?如果您导入一个调用此方法的函数会怎样......? (14认同)
  • @AndreySt 这实际上是有道理的。您的应用程序代码可能会随着时间的推移而发生变化。但是,如果任何给定迁移中使用的代码发生变化,则后续迁移可能会失败,如果您需要从头开始重建数据库,则会使您处于不一致的状态。 (4认同)
  • 与此相关的 Django doc 官方信息:https://docs.djangoproject.com/en/3.1/topics/migrations/#historical-models (2认同)

小智 14

你是否像文档中所说的那样打电话给你的模型?

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()
Run Code Online (Sandbox Code Playgroud)

数据迁移 因为此时您无法直接导入模型:

from yourappname.models import Person
Run Code Online (Sandbox Code Playgroud)

更新

内部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)
Run Code Online (Sandbox Code Playgroud)

在"假"模型实例中只有克隆字段:

MyModel.__module__ = '__fake__'
Run Code Online (Sandbox Code Playgroud)

Github Django

  • 是.您可以访问模型上的字段,但不能访问模型方法 (13认同)

use*_*759 11

精美的印刷品位于历史模型中

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

当我在迁移过程中第一次遇到它并且没有阅读细则时,我感到非常惊讶,因为它似乎与他们的设计理念相矛盾(在模型周围添加功能)


Rya*_*ght 8

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


djv*_*jvg 5

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

不仅自定义模型方法在迁移中不可用,其他模型属性也是如此,例如用于模型字段的类“常量” choices请参阅文档中的示例。

在这种特定的边缘情况下,我们无法在迁移期间直接访问选择的历史值,但我们可以使用model _meta api从模型字段获取历史值,因为这些值包含在迁移中。

给出 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,
    )
Run Code Online (Sandbox Code Playgroud)

Student.FRESHMAN我们可以通过如下方式获取内部迁移的历史值:

...
Student = apps.get_model('my_app', 'Student')
YEAR_IN_SCHOOL_CHOICES = Student._meta.get_field('year_in_school').choices
...
Run Code Online (Sandbox Code Playgroud)