Thursday, July 23, 2015

Re: Migrate a many-to-many field to an explicit intermediate ("through") model?

Hi Carsten,

On Thursday, July 16, 2015 at 12:44:55 PM UTC-6, Carsten Fuchs wrote:
Am 2015-07-16 um 18:15 schrieb Gergely Polonkai:
> 1) create the "through" model, add it to the migration file
> 2) migrate all m2m instances (e.g. iterate over all Bereich objects then
> iterate through its users, creating a UserBereichAssignment object for
> each (all this done in a migrations.RunPython call)

Ok. I've never used migrations.RunPython before, but I'm quite confident
that I can manage it.

You could also use RunSQL instead of RunPython for this data migration step - personally I often find that easier (and almost always more efficient) for a simple data migration, since all you need is a SQL query to copy some data from one table to another, you don't need any Python conveniences or model instances along the way.

But either can work, it really just depends how comfortable you are with raw SQL.
 
Would the migration for step 2) be a separate migration file from step
1), or is the migration file of step 1) edited to cover step 2) as well?

Separate migration. In general you can't put schema alterations and data migrations in the same migration file, because then they'll try to run in the same transaction, and PostgreSQL at least doesn't like that.

In general, for complex changes, this three-step dance (one migration to add the new field/table, a second migration to copy the data, and a third migration to remove the old field/table) is very common and the right way to do things.
 
> 3) change the m2m field by adding the through option, and add this
> change to the migrations file.

Same question: Is this supposed to go into a separate migration file or
into the common migrations file from above?

And won't this last step trigger the same error as before? ("... you
cannot alter to or from M2M fields, or add or remove through= on M2M
fields") ?

This part I'm not sure about without trying it. I'm honestly not sure what exactly Gergely was recommending. Based on my reading of the code, the error is raised by the schema-editor backend, meaning if you try an `AlterField` with this change, you'd hit the error.

Another possibility might be to use the `SeparateDatabaseAndState` operation to create a migration that has no effect on the schema, but just updates the Python state for the field. Since you've already made the necessary db schema changes simply by adding the through model itself, this should work fine.

You could also go back and edit the field definition in whichever migration initially created it. This would probably work, but it would cause problems for any `RunPython` migrations since then that used that field (because now they'd try to use the through table instead), including your own data migration that you just created prior to this. So probably not a good option.
 
(Not so important, but out of curiosity: This won't get us rid of the
old, implicit intermediate table, will it?)

No. You could add another RunSQL migration to remove this table using raw SQL.

Overall I think it might be simpler to go with your initial approach. I'd first rename the old field to some other temporary name (you don't have to update any other code to use this name, as you'll commit all of these migrations in one commit, so nothing but the following data migration ever needs to know anything about this temporary name), then add the new field with through table (using the original field name), then migrate data (using either RunPython or RunSQL), then remove the old field. I think this will work fine, and it should also remove the old auto-created through table.

Good luck! I think this is definitely an area where the migrations system could help a bit more.

Carl

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/e987cf79-358d-4fdc-bac5-5131a19a25c1%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment