【问题标题】:Django migrations RunPython not able to call model methodsDjango迁移RunPython无法调用模型方法
【发布时间】:2015-04-30 21:20:32
【问题描述】:

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

【问题讨论】:

    标签: django django-models django-migrations


    【解决方案1】:

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

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

    如果你有这个方法:

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

    这样迁移将起作用。会有一些代码重复,但没关系,因为数据迁移应该是应用程序特定状态下的一次性操作。

    【讨论】:

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

    您是否像文档中所说的那样调用您的模型?

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

    Data-Migration 因为此时,您无法直接导入您的模型:

    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

    【讨论】:

    • 是的。您可以访问模型上的字段,但不能访问模型方法
    • 赞成combine_names 中的有用评论。我完全忘记了你不能直接导入模型 - 在我被提醒之前,我做了很多头疼的尝试让迁移工作。
    【解决方案3】:

    细则在Historical Models

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 这对我有用!但请记住运行迁移以更新更改后的管理器 - 并将其作为您正在编写的数据迁移的依赖项。
      • @erikvw 你能详细说明一下吗?
      【解决方案5】:

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

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

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

      鉴于 Django 的Student example

      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
      ...
      

      【讨论】:

        猜你喜欢
        • 2020-05-22
        • 2015-04-10
        • 2021-08-01
        • 2021-09-08
        • 2014-07-01
        • 1970-01-01
        • 2019-09-15
        • 1970-01-01
        相关资源
        最近更新 更多