Opened 3 years ago

Last modified 18 months ago

#24260 new New feature

Add migration support for logical field renames

Reported by: Raymond Penners Owned by: nobody
Component: Migrations Version: 1.7
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Suppose you have a legacy model, defined like this:

    class Foo(models.Model):
        foo_id = models.AutoField(primary_key=True)

Now, I would like to clean this up, into:

    class Foo(models.Model):
        id = models.AutoField(primary_key=True, db_column='foo_id')

This is only a logical name change, without any impact on the
database. Yet, there seems to be no way to convince makemigrations
that nothing needs to happen.

Whatever I do, it will always complain, or worse, attempt to remove
the foo_id altogether:

    operations = [
        migrations.RemoveField(
            model_name='foo',
            name='foo_id',
        ),
        migrations.AddField(
            model_name='foo',
            name='id',
            field=models.AutoField(default=0, serialize=False, primary_key=True, db_column=b'foo_id'),
            preserve_default=False,
        ),
    ]

Using South you could work around this by having empty
forwards/backwards methods. It would be nice if Django migrations
offered a way out as well.

Change History (5)

comment:1 Changed 3 years ago by Tim Graham

Triage Stage: UnreviewedAccepted
Type: BugNew feature

I'm guessing you might be able to use the SeparateDatabaseAndState operation to achieve this, but if so, the documentation should really be improved with an example.

comment:2 Changed 3 years ago by Tim Graham

Actually, something like simply

migrations.AlterField(
    model_name='foo',
    name='id',
    field=models.AutoField(default=0, serialize=False, primary_key=True, db_column=b'foo_id'),
    preserve_default=False,
)

might work. Did you try that? Django needs a migration to know the field has changed, even if no SQL is generated.

comment:3 Changed 3 years ago by Raymond Penners

That does not seem to help. If I add the following migration:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

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

    operations = [
        migrations.AlterField(
            model_name='foo',
            name='id',
            field=models.AutoField(serialize=False, primary_key=True, db_column=b'foo_id'),
            preserve_default=False,
        ),
    ]

Then:

python manage.py makemigrations foo
You are trying to add a non-nullable field 'id' to foo without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows)
 2) Quit, and let me add a default in models.py
Select an option: 

Putting some dummy default=0 or something in the migration does not help either.

comment:4 Changed 3 years ago by Markus Holtermann

You should get it working with

    operations = [
        migrations.AlterField(
            model_name='foo',
            name='foo_id',
            field=models.AutoField(db_column='foo_id', primary_key=True, serialize=False),
        ),
        migrations.RenameField(
            model_name='foo',
            old_name='foo_id',
            new_name='id',
        ),
    ]

This can be semi-automated by first ensuring the field arguments are explicitly set for foo_id (foo_id = models.AutoField(primary_key=True, db_column='foo_id'), then running makemigrations followed by a rename (id = models.AutoField(primary_key=True, db_column='foo_id') and another makemigrations (the latter will ask if foo.foo_id is being renamed to foo.id).

I don't, however, see a way how Django could automatically detect these changes in a single run, given that we have to check for renamed fields first: https://github.com/django/django/blob/master/django/db/migrations/autodetector.py#L183-L186 . After all, we will end up with two operations.

comment:5 Changed 18 months ago by Simon Charette

Related to #24157 where the autodetector can't detect a rename when the field options are also changed.

Note: See TracTickets for help on using tickets.
Back to Top