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

你。 提交于 2020-01-02 10:30:21

问题


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 ?


回答1:


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.




回答2:


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),
    ]


来源:https://stackoverflow.com/questions/49221515/how-can-i-set-provide-a-default-value-while-django-migration

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!