Last Updated: February 25, 2016
·
3.088K
· exallium

Another Caveat when migrating to Django MPTT

It's important to stress something here.

Whenever you save a Django-MPTT model, it will, by default, rebuild the tree (in part)

What does this mean for your migrations? Well, let's say you've already got a parent-child based relationship created. You've gone through the Django-MPTT tutorial, set up your TreeForeignKey, etc, and now, you need a data migration to restructure your production database to use this new schema.

Originally, before I thought about this caveat, I had implemented something like so:

for obj in MyMPTTModel.objects.all():
    obj.lft, obj.rght, obj.parent = (0, 0, obj.old_parent)
    obj.save()
MyMPTTModel.tree.rebuild()

It's pretty apparent why this doesn't work, especially with my explanation above. Every time we save an object, the tree tries to rebuild. Because we are saving an object at a time, and not particularly in a clean order, we can end up having illegal node movements, and thus our migration will error out.

The solution? Don't use the object's default save method. All we want to do is to save the 3 fields worth of information to the database, without triggering a rebuild. So, we use the method of Model instead, with a simple change:

obj.save() # Old
Model.save(obj) # New

This ensures that none of the MPTT stuff is run (until we run rebuild manually), and there's no janky order checking or any other weird code or proxy classes that would only complicate and possibly break things on us.

Note, that this is good here because it's a single spot we don't want the rebuild to occur. In other cases, I would recommend using a proxy model.

Links!