开发者

ManyToManyField and South migration

开发者 https://www.devze.com 2023-03-07 13:23 出处:网络
I have user profile model with M2M field class Account(models.Model): ... friends = models.ManyToManyField(\'self\', symmetrical=True, blank=True)

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')

Shoul开发者_高级运维d 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


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.


The way you've got it coded there, you're manually defining a model which does the same job as the m2m join table that Django will have automatically created for you. The thing is, the automatically created table will be called accounts_relationship_friend.

So, what you're doing there will create a model that tries to duplicate what the ORM has done under the surface, but it's pointing at the wrong table.

If you don't need an explicit join model, I would leave remove it from your codebase and not create a migration to add it, and instead use the M2M to find relationships between friends. (I'm not thinking about this too deeply, but it should work).

If, however, you want to do something special with the Relationship model you have (eg store attributes about the type of relationship, etc), I would declare the Relationship model to be the through model you use in your Friend.friends m2m definition. See the docs here.

0

精彩评论

暂无评论...
验证码 换一张
取 消