Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding a "through" table to django field and migrating with South?

Seems like this should be "easy" or at least documented somewhere, I just cant find it.

Lets say I have a model:

class A(models.Model):
    users = models.ManyToMany('auth.User', blank=True)

Now I want to migrate to have a through table to add fields to the ManyToMany relation...

class AUsers(models.Model):
    user = models.ForeignKey('auth.User')
    a = models.ForeignKey('A')
    new_field = models.BooleanField()

class A(models.Model):
    users = models.ManyToMany('auth.User', blank=True, through='AUsers')

Then I do:

% ./manage.py schemamigration app --auto

Not totally surprising, it tells me it is going to drop the original auto-created through table and create a new one for AUsers. What's the best practice at this point? Is there a decent way to migrate to the new through table? Do I use db_table in Meta? Do I just not use the through=... right away... then do a schemamigration --auto, then a datamigration to copy the current table (somehow, not sure...) and then add the through relation and let it kill the table?

What's the trick here? Is this really that hard?

like image 609
dlamotte Avatar asked May 19 '11 18:05

dlamotte


People also ask

What is the difference between Makemigrations and migrate in Django?

makemigrations is responsible for packaging up your model changes into individual migration files - analogous to commits - and migrate is responsible for applying those to your database.

How does Django migration work?

Django uses a database table called django_migrations . Django automatically creates this table in your database the first time you apply any migrations. For each migration that's applied or faked, a new row is inserted into the table. As you can see, there is an entry for each applied migration.

How do I merge migrations in Django?

Merging migrations There are several ways of doing it. The following is in the recommended order: The most simple fix for this is by running the makemigrations command with a --merge flag. This will create a new migration solving the previous conflict.


1 Answers

You should be able to do this pretty easily.

First of all, make sure that the manual through table that you are creating has the same table name in the database as the one Django originally created automatically.

So, first, let's consider a manual through model before your change:

class AUsers(models.Model):
    user = models.ForeignKey('auth.User')
    a = models.ForeignKey('A')

    class Meta:
        db_table = 'appname_a_user'

That should be functionally (almost) identical to the ManyToManyField you used to have. Actually, you could make an empty migration and apply it, and then use --auto for your changes (but don't).

Now, add your field like you did in your sample code above, and then run ./manage.py schemamigration appname manual_through_table --empty. That will give you an empty migration named ####_manual_through_table.py.

In the migration itself, there will be a forwards and backwards method. Each one needs to be one line each:

def forwards(self, orm):
    db.add_column('appname_a_user', 'new_field', self.gf('django.db.models.fields.BooleanField')(default=False))

def backwards(self, orm):
    db.delete_column('appname_a_user', 'new_field')

That should get you what you are after.

like image 67
Luke Sneeringer Avatar answered Oct 27 '22 21:10

Luke Sneeringer