Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to migrate with south when using 'through' for a ManyToMany field?

I want to change a ManyToMany field in a django app.

I have the following models:

class A:
    ...

class B:
    as = models.ManyToManyField(A)

and I want:

class A:
    ...

class C:
     a = models.ForeignKey(A)
     b = models.ForeignKey("B")
     extra_data = models.BooleanField(default=False)

class B:
    as = models.ManyToManyField(A, through=C)

I am using south for db migrations. Unfortunately, in this case, south propse to delete the existing app_b_a table and to recreate a new app_c.

Is there a way to say to south not to recreate the m2m table?

like image 904
luc Avatar asked Oct 24 '11 16:10

luc


2 Answers

I would try to do it the following way:

  1. Add the C model but do not add any extra data fields to it yet. Just set the 2 FK's to the linked models, so that it's schema is effectively identical to the default m2m table, except table name.
  2. Run schemamigration --auto for your app.
  3. To effectively "delete" the existing table and "create" the new one, without moving data, just delete all the generated migration code from forwards/backwards methods and add: forwards: db.rename_table('yourappname_m2mtablename', 'yourappname_c') backwards: db.rename_table('yourappname_c', 'yourappname_m2mtablename')
  4. Leave the "frozen" models dict as it is!
  5. Now you can extend your C model and generate a new schemamigration for it.
like image 108
Botond Béres Avatar answered Nov 16 '22 13:11

Botond Béres


Just had to do this myself recently. It's actually fairly easy.

First, add the new through model to your code, but don't specify it as the through parameter to your ManyToManyField yet. Create an auto schemamigration, which will create the new table for you.

$ python manage.py schemamigration --auto yourapp

Second, create a datamigration:

$ python manage.py datamigration yourapp name_of_migration

In your forwards migration:

for b in orm.B.objects.all():
    for a in b.as.all():
        orm.C.objects.create(a=a, b=b)

In your backwards migration:

for c in orm.C.objects.all():
    c.b.as.add(a)

Finally, add the through parameter to your ManyToManyField, and generate another auto schemamigration:

$ python manage.py schemamigration --auto yourapp
like image 2
Chris Pratt Avatar answered Nov 16 '22 13:11

Chris Pratt