'How can I set/provide a default value while django migration?

Scenario:
I have a model, Customer

class Customer(models.Model):
    name = models.CharField(max_length=100)
    age = models.IntegerField()
    company = models.CharField(max_length=100)


and now I updated the company attribute witha ForeignKey relationship as below,

class Company(models.Model):
    name = models.CharField(max_length=100)
    location = models.CharField(max_length=100)


class Customer(models.Model):
    name = models.CharField(max_length=100)
    age = models.IntegerField()
    company = models.ForeignKey(Company)



What I need is, when the new migrations applied to the DB,corresponding Company instance must automatically generate and map to the company attribute of Customer instance.

Is that possible? How can I achieve this ?



Solution 1:[1]

Sure, but you have to do three migrations and the fields cant be named the same thing as both need to exist at the same time. If you already have removed the company field in your real database you are SOL and will have to fix them manually.

First, add the Company model in a normal db migration, then do a data migration and have it run after the first db migration, then do another db migration removing the company field from the Customer model.

The db migrations you can do with manage.py makemigrations as usual, just add something like below in a migration file between them, here i named the new company ForeignKey field to company_obj

def fix_companies(apps, schema_editor):
    Company = apps.get_model("myapp", "Company")
    Customer = apps.get_model("myapp", "Customer")
    for c in Customer.objects.all():
        company, _ = Company.objects.get_or_create(name=c.name)
        c.company_obj = company
        c.save()


def rev(apps, schema_editor):
    # the reverse goes here if you want to copy company names into customer again if you migrate backwards.
    pass

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', 'XXXX_migration_that_added_company_model'),
    ]

    operations = [
        migrations.RunPython(fix_companies, rev),
    ]

Solution 2:[2]

Something to note is if you are going through a cycle of renaming/ deprecating fields, using RunPython would leave you pointing to old model fields that wouldn't exist anymore after you are done with your field changes.

To avoid this, you might want to go with RunSQL instead.

# Generated by Django 3.2.3 on 2022-02-09 04:55

from django.db import migrations


class Migration(migrations.Migration):

    dependencies = [
        ("<your_app>", "<0006_migration_name>"),
    ]

    operations = [
        migrations.RunSQL(f"""
update public.<table_name> set new_field = old_field + some_magic;
"""
        ),
    ]

Docs.

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
Solution 2 Karuhanga