Using native migrations in Django has been a very positive experience. Now and then however, some thing appear difficult or impossible. In this post, I will demonstrate how migrations can be used to move from an automatic ManyToMany relation in Django to an explicit relation.
Lets start by creating a small app with a ManyToMany relation.
Suppose we want some way to order the list of
Bar instances in the relation. In setting up the ManyToMany relation, Django has created a table for us, named
through_foo_bars. We can see what it looks like in python using the
inspectdb command
$ ./manage.py inspectdb through_foo_bars
We can make explicit the automatic relation that Django has created in the form of an extra table, by adding the model to our application.
Note the
ordering we specified in the Meta definition. We told Django to use our new model for the relation by specifying it as the
through argument in the
ManyToManyField.
Django is happy to create a migration for us, incorporating the change.
$ ./manage.py makemigrations
The problem starts when we try to execute the migrations
$ ./manage.py migrate
...
ValueError: Cannot alter field through.Foo.bars into through.Foo.bars - they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)
So according to Django this kind of migration is not possible.
Let's analyse what the problem is. The below migration tries to create a new model called ThroughFooBars and then change the
ManyToManyField on
Foo.
The problem is ofcourse that no new model needs to be created because the table allready exists.
In this case we
DO want to change the migration state, altering the relation, but we
do NOT want to change the database schema.
We are lucky that django offers such migrations by providing us with
migrations.SeparateDatabaseAndState. We will first modify the state to make
the relation explicit, after which we can do the modifications to the schema to add the ordering. In the migration below, you can see we first modify the state, making the relation explicit in the state only, without modifying the database schema. Next we add the order field and the ordering Meta definition.
We can check if the migration was succesful with inspectdb:
$ ./manage.py inspectdb through_foo_bars
Seems to look good to me?
Both migrations file are equal
ReplyDeleteI fixed it. github wont let you embed a different revision of a file than the latest :\
Delete