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: <pre>paymentmodel_ptr = models.IntegerField(null=True)</pre>
- 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!): <pre>python manage.py datamigration your_app_name \ populate_me_pointers</pre>
- Edit the datamigration: <pre>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()</pre>
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.</li> * Edit your model to (1) remove the parentmodel_ptr _and at the same time_ add the new parent class. Then run another schemamigration.</ol> 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..._