Search code examples
djangodjango-modelsdjango-migrations

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

  • Let's start from your original model and do it step by step.

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

    First you would have to keep the original field and create a new one, to be able to restore the old data afterwards.

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

    Now you can create a first migration with manage.py makemigrations. Then you will have to create a data migration. Create the migration using manage.py makemigrations yourapp --empty and update the generated file:

    from django.db import migrations
    
    def export_customer_company(apps, schema_editor):
        Customer = apps.get_model('yourapp', 'Customer')
        Company = apps.get_model('yourapp', 'Company')
        for customer in Customer.objects.all():
            customer._company = Company.objects.get_or_create(name=customer.company)[0]
            customer.save()
    
    def revert_export_customer_company(apps, schema_editor):
        Customer = apps.get_model('yourapp', 'Customer')
        Company = apps.get_model('yourapp', 'Company')
        for customer in Customer.objects.filter(_company__isnull=False):
            customer.company = customer._company.name
            customer.save()
    
    class Migration(migrations.Migration):
    
        dependencies = [
            ('yourapp', 'xxxx_previous_migration'),  # Note this is auto-generated by django
        ]
    
        operations = [
            migrations.RunPython(export_customer_company, revert_export_customer_company),
        ]
    

    The above migration will populate your Company model and Customer._company field according to Customer.company.

    Now you can drop the old Customer.company and rename Customer._company.

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

    Final manage.py makemigrations and manage.py migrate.