2

I have a very similar situation like this: Django migration strategy for renaming a model and relationship fields

I need to rename Foo to Bar.

We have an identical myapp:

class Foo(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)

But I have in my myotherapp a ManyToMany field:

class AnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_awesome = models.BooleanField()

class YetAnotherModel(models.Model):
    foo = models.ManyToManyField(Foo, blank=True, null=True) # Here!
    is_ridonkulous = models.BooleanField()

I tried rename:

foo = models.ManyToManyField(Foo, blank=True, null=True)

to IntegerField() but doesn't work. How can I do that?

Community
  • 1
  • 1
Giovanni Cornachini
  • 151
  • 1
  • 3
  • 11

1 Answers1

3

This is how I did it. Note: I was NOT in production, so I did not have to worry about information already in the tables. If you currently have data that you need to keep in the linking table, back up your data first. Also, I was using Django 1.9, but I think that everything referenced here is in 1.8 too.

The issue with the many-to-many relationship is the intermediate tables. Using RemoveField and AddField handled that.

The myapp migration for the model rename probably looks something like this:

class Migration(migrations.Migration):

    dependencies = [
        ('Foo', '0001_initial.py'),
    ]

    operations = [
        migrations.RenameModel(
            old_name='Foo',
            new_name='Bar',
        ),
    ]

Next you would run:

python manage.py makemigrations --empty myotherapp

Then you would put this code in the new migration:

dependencies = [
        ('myotherapp', '0001_initial.py'),
        ('myapp', '0002_whateverthismigrationwasnamed')
    ]

    operations = [
        migrations.RemoveField(
            model_name='YetAnotherModel',
            name='Foo'
        ),
        migrations.AddField(
            model_name='YetAnotherModel',
            name='Bar',
            field=models.ManyToManyField(blank=True, null=True, to='myapp.Bar'),
        ),
    ]

It's important to make sure that you add the rename model migration from myapp as a dependency so it runs first.

Now, if you are in production you should NOT use this method without taking precautions. It straight up deletes the linking table. This from the django docs on RemoveField:

Bear in mind that when reversed, this is actually adding a field to a model. The operation is reversible (apart from any data loss, which of course is irreversible) if the field is nullable or if it has a default value that can be used to populate the recreated column.

If you are in production, you will want to take steps to backup the data so you can restore it into the new table.

Kryštof Řeháček
  • 1,965
  • 1
  • 16
  • 28
Fred
  • 563
  • 3
  • 12