Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django migration strategy for renaming a model and relationship fields

People also ask

How do I rename a Django model?

In the current version of Django you can rename the model and run the python manage.py makemigrations , django will then ask if you want to rename the model and if you select yes, then all renaming process will be done automatically.

How do you add a new field to a model with new Django migrations?

To answer your question, with the new migration introduced in Django 1.7, in order to add a new field to a model you can simply add that field to your model and initialize migrations with ./manage.py makemigrations and then run ./manage.py migrate and the new field will be added to your DB.

Can I rename Django migration file?

For migrations that haven't been applied to the database, you can rename a migration file directly in the migrations folder to a more descriptive name. At this point, the migration file is just a representation of model changes that no one else knows about, so you can even delete the migration file if needed.

What command should you use to create a migration from the Django models?

Create or update a model. Run ./manage.py makemigrations <app_name> Run ./manage.py migrate to migrate everything or ./manage.py migrate <app_name> to migrate an individual app. Repeat as necessary.


So when I tried this, it seems you can condense Step 3 - 7:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'), 
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar'),
        migrations.RenameField('AnotherModel', 'foo', 'bar'),
        migrations.RenameField('YetAnotherModel', 'foo', 'bar')
    ]

You may get some errors if you don't update the names where it's imported e.g. admin.py and even older migration files (!).

Update: As ceasaro mentions, newer versions of Django are usually able to detect and ask if a model is renamed. So try manage.py makemigrations first and then check the migration file.


At first, I thought that Fiver's method worked for me because the migration worked well until step 4. However, the implicit changes 'ForeignKeyField(Foo)' into 'ForeignKeyField(Bar)' was not related in any migrations. This is why migration failed when I wanted to rename relationship fields (step 5-8). This might be due to the fact that my 'AnotherModel' and 'YetAnotherModel' are dispatched in other apps in my case.

So I managed to rename my models and relationship fields doing following below steps:

I adapted the method from this and particularly the trick of otranzer.

So like Fiver let's say we have in myapp:

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

And in myotherapp:

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


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_ridonkulous = models.BooleanField()

Step 1:

Transform every OneToOneField(Foo) or ForeignKeyField(Foo) into IntegerField(). (This will keep the id of related Foo object as value of the integerfield).

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

class YetAnotherModel(models.Model):
    foo = models.IntegerField()
    is_ridonkulous = models.BooleanField()

Then

python manage.py makemigrations

python manage.py migrate

Step 2: (Like step 2-4 from Fiver)

Change the model name

class Bar(models.Model):  # <-- changed model name
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)

Create an empty migration:

python manage.py makemigrations --empty myapp

Then edit it like:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

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

Eventually

python manage.py migrate

Step 3:

Transform Back your IntegerField() into their previous ForeignKeyField or OneToOneField but with the new Bar Model. (The previous integerfield was storing the id, so django understand that and reestablish the connection, which is cool.)

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

class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Bar)
    is_ridonkulous = models.BooleanField()

Then do:

python manage.py makemigrations 

Very importantly, at this step you have to modify every new migrations and add the dependency on the RenameModel Foo-> Bar migrations. So if both AnotherModel and YetAnotherModel are in myotherapp the created migration in myotherapp must look like this:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '00XX_the_migration_of_myapp_with_renamemodel_foo_bar'),
        ('myotherapp', '00xx_the_migration_of_myotherapp_with_integerfield'),
    ]

    operations = [
        migrations.AlterField(
            model_name='anothermodel',
            name='foo',
            field=models.ForeignKey(to='myapp.Bar'),
        ),
        migrations.AlterField(
            model_name='yetanothermodel',
            name='foo',
            field=models.ForeignKey(to='myapp.Bar')
        ),
    ]

Then

python manage.py migrate

Step 4:

Eventually you can rename your fields

class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar) <------- Renamed fields
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar) <------- Renamed fields
    is_ridonkulous = models.BooleanField()

and then do automatic renaming

python manage.py makemigrations

(django should ask you if you actually renamed the modelname, say yes)

python manage.py migrate

And that's it!

This works on Django1.8


I needed to do the same thing and follow. I changed the model all at once (Steps 1 and 5 together from Fiver's answer). Then created a schema migration but edited it to be this:

class Migration(SchemaMigration):
    def forwards(self, orm):
        db.rename_table('Foo','Bar')

    def backwards(self, orm):
        db.rename_table('Bar','Foo')

This worked perfectly. All my existing data showed up, all the other tables referenced Bar fine.

from here: https://hanmir.wordpress.com/2012/08/30/rename-model-django-south-migration/


For Django 1.10, I managed to change two model class names (including a ForeignKey, and with data) by simply running Makemigrations, and then Migrate for the app. For the Makemigrations step, I had to confirm that I wanted to change the table names. Migrate changed the names of the tables without a problem.

Then I changed the name of the ForeignKey field to match, and again was asked by Makemigrations to confirm that I wanted to change the name. Migrate than made the change.

So I took this in two steps without any special file editing. I did get errors at first because I forgot to change the admin.py file, as mentioned by @wasibigeek.


I also faced the problem as v.thorey described and found that his approach is very useful but can be condensed into fewer steps which are actually step 5 to 8 as Fiver described without step 1 to 4 except that step 7 needs to be changed as my below step 3. The overall steps are as follow:

Step 1: Edit the related field names in models.py

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


class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_ridonkulous = models.BooleanField()

Step 2: Create an empty migration

python manage.py makemigrations --empty myapp

Step 3: Edit the Migration class in the migration file created in Step 2

class Migration(migrations.Migration):

dependencies = [
    ('myapp', '0001_initial'), 
]

operations = [
    migrations.AlterField(
        model_name='AnotherModel',
        name='foo',
        field=models.IntegerField(),
    ),
    migrations.AlterField(
        model_name='YetAnotherModel',
        name='foo',
        field=models.IntegerField(),
    ),
    migrations.RenameModel('Foo', 'Bar'),
    migrations.AlterField(
        model_name='AnotherModel',
        name='foo',
        field=models.ForeignKey(to='myapp.Bar'),
    ),
    migrations.AlterField(
        model_name='YetAnotherModel',
        name='foo',
        field=models.ForeignKey(to='myapp.Bar'),
    ),
    migrations.RenameField('AnotherModel', 'foo', 'bar'),
    migrations.RenameField('YetAnotherModel', 'foo', 'bar')
]

Step 4: Apply the migration

python manage.py migrate

Done

P.S. I've tried this approach on Django 1.9


I am using Django version 1.9.4

I have follow the following steps:-

I have just rename the model oldName to NewName Run python manage.py makemigrations. It will ask you for Did you rename the appname.oldName model to NewName? [y/N] select Y

Run python manage.py migrate and it will ask you for

The following content types are stale and need to be deleted:

appname | oldName
appname | NewName

Any objects related to these content types by a foreign key will also be deleted. Are you sure you want to delete these content types? If you're unsure, answer 'no'.

Type 'yes' to continue, or 'no' to cancel: Select No

It rename and migrate all existing data to new named table for me.