Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django migrations RunPython not able to call model methods

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 defined on a model using RunPython?

like image 778
user2954587 Avatar asked Feb 28 '15 02:02

user2954587


People also ask

What is difference between migrate and Makemigrations in Django?

makemigrations is responsible for packaging up your model changes into individual migration files - analogous to commits - and migrate is responsible for applying those to your database.

How do you add a new field to a model with new Django migrations?

To answer your question, with the new migration introduced in Django 1.7, in order to add a new field to a model you can simply add that field to your model and initialize migrations with ./manage.py makemigrations and then run ./manage.py migrate and the new field will be added to your DB.

What does fake migration do in Django?

--fake-initialAllows Django to skip an app's initial migration if all database tables with the names of all models created by all CreateModel operations in that migration already exist. This option is intended for use when first running migrations against a database that preexisted the use of migrations.


2 Answers

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.

like image 63
chhantyal Avatar answered Sep 18 '22 01:09

chhantyal


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

like image 28
Azman0101 Avatar answered Sep 18 '22 01:09

Azman0101