Django migrations RunPython not able to call model methods

前端 未结 4 466
感情败类
感情败类 2020-12-04 23:04

I\'m creating a data migration using the RunPython method. However when I try to run a method on the object none are defined. Is it possible to call a method de

相关标签:
4条回答
  • 2020-12-04 23:48

    As of Django 1.8, you can make model managers available to migrations by setting use_in_migrations = True on the model manager. See the migrations documentation.

    0 讨论(0)
  • 2020-12-04 23:50

    The fine print is laid in Historical Models

    Because it’s impossible to serialize arbitrary Python code, these historical models will not have any custom methods that you have defined.

    It was quite a surprise when I first encountered it during migration and didn't read the fine print because it seems to contradict their Design Philosophy (adding functions around models)

    0 讨论(0)
  • 2020-12-04 23:51

    Model methods are not available in migrations, including data migrations.

    However there is workaround, which should be quite similar to calling model methods. You can define functions inside migrations that mimic those model methods you want to use.

    If you had this method:

    class Order(models.Model):
        '''
        order model def goes here
        '''
    
        def get_foo_as_bar(self):
            new_attr = 'bar: %s' % self.foo
            return new_attr
    

    You can write function inside migration script like:

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

    Then use it in migrations:

    class Migration(migrations.Migration):
    
        dependencies = [
            ('order', '0001_initial'),
        ]
    
        operations = [
            migrations.RunPython(save_foo_as_bar)
        ]
    

    This way migrations will work. There will be bit of repetition of code, but it doesn't matter because data migrations are supposed to be one time operation in particular state of an application.

    0 讨论(0)
  • 2020-12-05 00:11

    did you call your model like said in the documentation ?

    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 Because at this point, you can't import your Model directly :

    from yourappname.models import Person
    

    Update

    The internal Django code is in this file 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)
    

    There is only fields that are clones in a "fake" model instance:

    MyModel.__module__ = '__fake__'
    

    Github Django

    0 讨论(0)
提交回复
热议问题