我正在使用RunPython
方法创建数据迁移。但是,当我尝试在对象上运行方法时,没有一个被定义。是否可以使用RunPython
调用在模型上定义的方法?
我正在使用RunPython
方法创建数据迁移。但是,当我尝试在对象上运行方法时,没有一个被定义。是否可以使用RunPython
调用在模型上定义的方法?
模型方法在迁移中不可用,包括数据迁移。
但是有一种解决方法,可以相当于调用模型方法。您可以在迁移中定义函数来模仿要使用的那些模型方法。
如果您有这个方法:
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/db/migrations/state.py 包含了 Django 内部代码,其中的 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
中,对于有用的评论点赞。我完全忘记了不能直接导入模型,之前曾经在尝试让迁移正常运行时犯了很多难题,直到被提醒才想起这一点。 - Jihoon Baek从Django 1.8版本开始,您可以通过在模型管理器上设置use_in_migrations = True
来使模型管理器在迁移中可用。请参阅迁移文档。
这并没有回答问题,但可能对某些人仍然有用。
不仅自定义模型方法在迁移中无法使用,其他模型属性也同样如此,例如用于模型字段choices
的类“常量”。请参见文档中的示例。
在这种特定的边缘情况下,在迁移期间我们无法直接访问选择的历史值historical,但是我们可以使用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,
)
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)
]
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)
,不需要加上括号 "()"。
apps.get_model()
的第一个参数是应用程序名称。请参阅文档https://docs.djangoproject.com/en/4.2/topics/migrations/#data-migrations - undefined