How to perform datamigration when moving from one to one to one to many

I use south to manage migrations and I hit a corner. I basically have the following setting:

App1

 class A(models.Model): # bunch of attributes 

App2

 class B(models.Models): instance_a = models.OneToOneField(A, null=True, blank=True, editable=False) 

Now I want to go to the following:

App1

 class A(models.Model): instance_b = models.ForeignKey(B, null=True, blank=True) 

App2

 class B(models.Models): # other attributes 

My main problem is that I cannot lose data. Thus, basically at the end of the migration (s), all objects A that were previously attached to objects B should maintain this mapping. As an example, if object A with identifier 7 was mapped to object B with identifier 8, by the end of this procedure this mapping should be preserved.

I tried several things from schema migrations mixed with temporary placeholders and data migrations. However, I always find myself in the same place that was at the time of the data transfer. I no longer have the same relationship to access the correct attributes. So, for example, B.instance_a is no longer available.

I would like your opinion on two things:

  • Firstly, it is viable in general, using only southern migrations.
  • Secondly, how should I continue.

thanks

+4
source share
1 answer

Finally, after a while I got a procedure with django-south , which may help others. The key was in the south depends_on (http://south.aeracode.org/wiki/Dependencies). I did this in 4 steps:

First

  • Create a placeholder for foreign key values ​​in model A

So model A becomes:

 class A(models.Model): instance_b_placeholder = models.ForeignKey(A, null=True, blank=True) 

Now just run manage.py schemamigration app1 --auto .

Second

  • Create a datamigration so that we can copy the values. The goal is to duplicate the data in db and then rename the attributes and delete the old ones. Error manage.py datamigration app1 update_fields . I decided to keep datamigration in app1 . If you do not, just make sure it starts after the previous migration.

The data encoding is encoded here:

 # Forwards: for b in orm['app2.B'].objects.filter(instance_b__isnull=False): b.instance_a.instance_b_placeholder = b b.instance_a.save() # Backwards: for r in orm['app1.A'].objects.filter(instance_b_placeholder__isnull=False): r.instance_b_placeholder.instance_a = r r.instance_b_placeholder.save() 

Third

  • Remove the instance_b field from model B and be sure to make a migration run after the one created in the previous step.

Model B becomes:

 class B(models.Model): # etc... 

The problem is manage.py schemamigration app2 --auto and edit the migration by adding the previous migration to depends_on :

 depends_on = ( ("app1", "<migration_number>_update_fields"), ) 

Fourth step :

  • Rename the owner’s place. This is achieved by changing the name in the code and editing the migration. Editing is necessary since south tends to delete and add a new column, but we only want it to rename the column.

  • This migration should work in last place, so I made it dependent on the previous one.

Here is the code:

 depends_on = ( ("app2", "<previous_migration_here>"), ) # Forwards: db.rename_column('app1_a', 'instance_b_placeholder_id', 'instance_b_id') # Backwards: db.rename_column('app1_a', 'instance_b_id', 'instance_b_placeholder_id') 

So what is it. I don’t know if there are so many other ways to do this, but at least it helped me.

+3
source

All Articles