I recently wanted to introduce a new parent model for some of my Django models. (The model is called “PayableModel” and is used for any model for which you might pay money.)
Like any reasonable person, I use South to manage my models. This is a Good Thing.
However, much like changing AUTH_USER_MODEL, there is not much support for how to go through this process and keep your existing data. So, in the hopes it’s useful, here’s what I did.
Note: this is a one-way migration. Your model’s ‘id’ field is going to be dropped, and it’s pretty hard to get that back (although now I have some ideas). In any case…
- Create your new parent model and run schemamigration as normal (python manage.py schemamigration your_app_name –auto)
- In the to-be child model(s), create a new nullable IntegerField called parentmodel_ptr. For example:
paymentmodel_ptr = models.IntegerField(null=True)
- Run schemamigration again. If you’re into that sort of thing, add a “depends_on” clause to the migration to make it dependent on the migration from step #1.
- Create a datamigration. (Fancy!):
python manage.py datamigration your_app_name \ populate_me_pointers
- Edit the datamigration:
def forwards(self, orm): for child in orm.ChildModel.objects.all(): # in which we create the parent: parent = orm['parent.ParentModel']() parent.save() child.parentmodel_ptr = parent.pk child.save()
This snazzily creates the parents and links it to the secret Django magic _ptr field that will tell Django where to find the parent. At this point, the field is nothing special.
- Edit your model to (1) remove the parentmodel_ptr and at the same time add the new parent class. Then run another schemamigration.
The brilliance of this model is I have idea how South converts the IntegerField to the foreign key. It will see the field as changing, rather than being deleted and re-added, because you made the change all at once.
I hope this helps!
If there was one post that made me want to switch to a Markdown-based blog…