'Django change an existing field to foreign key

I used to have a model like this:

class Car(models.Model):
    manufacturer_id = models.IntegerField()

There is another model Manufacturer that the id field refers to. However, I realized that it would be useful to use django's built-in foreign key functionality, so I changed the model to this:

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer)

This actually works fine immediately, queries work without errors, everything is great, except that if I try to run migrations, Django outputs the following:

 - Remove field manufacturer_id from car
 - Add field manufacturer to car

Doing this migration would clear all the existing relationships in the database, so I don't want to do that. I don't really want any migrations at all, since queries like Car.objects.get(manufacturer__name="Toyota") work fine. I would like a proper database foreign key constraint, but it's not a high priority.

So my question is this: Is there a way to make a migration or something else that allows me to convert a existing field to a foreign key? I can't use --fake since I need to reliably work across dev, prod, and my coworkers computers.



Solution 1:[1]

You can do data migration

  1. add new field
  2. do a data migration https://docs.djangoproject.com/en/3.1/topics/migrations/#data-migrations
  3. remove old field

I am not sure, there might be another solution where you can rename the field to name you want to, then alter the filed to new type (do a migration)

operations = [
        migrations.RenameField(
            model_name='car',
            old_name='manufacturer_id',
            new_name='manufacturer',
        ),
        migrations.AlterField(
            model_name='car',
            name='manufacturer',
            field=ForeignKey(blank=True, null=True,  
                  on_delete=django.db.models.deletion.CASCADE
            ),
    ]

Solution 2:[2]

I've faced the same issue today. Can be done without renaming an existing field or dropping existing column.

  1. Update the initial migration with CreateModel operation
...
        migrations.CreateModel(
            name="Car",
            fields=[
                (
                    "manufacturer",
                    models.ForeignKey(
                        blank=True,
                        null=True,
                        on_delete=django.db.models.deletion.DO_NOTHING,
                        db_constraint=False,
                        db_index=False,
                        to="Manufacturer",
                    ),
                ),
  1. Check that db_constraint and db_index are True on Car.manufacturer field of Car model. True is a default value if the fields not set.
  2. Run ./manage.py makemigrations that generates AlterField migration with required constraint and index on Car.manufacturer_id.

The first step won't effect db consistency because the generated DDL will be the same for IntegerField and ForeignKey with db_constaint=False and db_index=False and the second migration adds missing constraint and index.

You can check that with ./manage.py sqlmigrate app migration command

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 mousetail
Solution 2