问题
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