Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ManyToManyField and South migration

I have user profile model with M2M field

class Account(models.Model):
    ...
    friends = models.ManyToManyField('self', symmetrical=True, blank=True)
    ...

Now I need to know HOW and WHEN add each other as a FRIEND And I created a model for that

class Account(models.Model):
    ...
    friends = models.ManyToManyField('self', symmetrical=False, blank=True, through="Relationship")
    ...


class Relationship(models.Model):    
    """ Friends """        
    from_account = models.ForeignKey(Account, related_name="relationship_set_from_account")            
    to_account = models.ForeignKey(Account, related_name="relationship_set_to_account")
    # ... some special fields for friends relationship

    class Meta:                    
        db_table = "accounts_account_friends"            
        unique_together = ('from_account','to_account')

Should I create any migration for this changes or not ? If you have any suggestions you are feel free write their here.

Thanks

PS: accounts_account table already contain records

like image 429
srusskih Avatar asked May 23 '11 07:05

srusskih


1 Answers

First off, I'd avoid using the db_table alias if you can. This makes it harder to understand the table structure, as it is no longer in sync with the models.

Secondly, the South API offers functions like db.rename_table(), which can be used by manually editing the migration file. You can rename the accounts_account_friends table to accounts_relation (as Django would name it by default), and add the additional columns.

This combined gives you the following migration:

def forwards(self, orm):
    # the Account.friends field is a many-to-many field which got a through= option now.
    # Instead of dropping+creating the table (or aliasing in Django),
    # rename it, and add the required columns.

    # Rename table
    db.delete_unique('accounts_account_friends', ['from_account', 'to_account'])
    db.rename_table('accounts_account_friends', 'accounts_relationship')

    # Add extra fields
    db.add_column('accounts_relationship', 'some_field',  ...)

    # Restore unique constraint
    db.create_unique('accounts_relationship', ['from_account', 'to_account'])


def backwards(self, orm):

    # Delete columns
    db.delete_column('accounts_relationship', 'some_field')
    db.delete_unique('accounts_relationship', ['from_account', 'to_account'])

    # Rename table
    db.rename_table('accounts_relationship', 'accounts_account_friends')
    db.create_unique('accounts_account_friends', ['from_account', 'to_account'])


models = {
    # Copy this from the final-migration.py file, see below
}

The unique relation is removed, and recreated so the constraint has the proper name.

The add column statements are easily generated with the following trick:

  • Add the Relationship model in models.py with foreign key fields only, and no changes to the M2M field yet.
  • Migrate to it
  • Add the fields to the Relationship model.
  • Do a ./manage.py schemamigration app --auto --stdout | tee final-migration.py | grep column
  • Revert the first migration.

Then you have everything you need to construct the migration file.

like image 168
vdboor Avatar answered Nov 15 '22 21:11

vdboor