【问题标题】:How can I set/provide a default value while django migration?如何在 django 迁移时设置/提供默认值?
【发布时间】:2018-08-19 15:55:34
【问题描述】:

场景
我有一个模型,Customer

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


现在我用ForeignKey 关系更新了company 属性,如下所示,

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)



我需要的是,当新的迁移应用到数据库时,对应的Company 实例必须自动生成并映射到Customer 实例的company 属性。

这可能吗?我怎样才能做到这一点?

【问题讨论】:

    标签: django django-models django-migrations


    【解决方案1】:

    让我们从你原来的模型开始,一步一步来。

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

    首先,您必须保留原始字段并创建一个新字段,然后才能恢复旧数据。

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

    现在您可以使用manage.py makemigrations 创建第一个迁移。然后你必须创建一个data migration。使用manage.py makemigrations yourapp --empty 创建迁移并更新生成的文件:

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

    上述迁移将根据Customer.company 填充您的Company 模型和Customer._company 字段。

    现在您可以删除旧的Customer.company 并重命名Customer._company

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

    最终manage.py makemigrationsmanage.py migrate

    【讨论】:

    • 几乎! customer._company = Company.objects.get_or_create(name=customer.company) 无法正常工作,因为 get_or_create 返回带有 (object, created) 的元组
    • 我可以问你一些事情,noop() 方法的目的是什么?
    • 这是一个什么都不做的方法。这意味着在逆转迁移时无需执行任何操作。我邀请您阅读我发布的关于迁移的链接。我使用的所有东西都在这里详细说明。 :)
    • 我按照你说的应用了迁移。然后我恢复为零,(python manage.py app_name zero)。然后我再次迁移,此时我得到了一些operational error
    • 迁移一开始就成功了吗?你归零的原因有哪些?什么迁移导致了操作错误?欢迎提供操作错误的详细信息。
    【解决方案2】:

    当然可以,但是您必须进行三个迁移,并且字段不能被命名为相同的东西,因为两者需要同时存在。如果您已经在真实数据库中删除了公司字段,那么您就是 SOL,并且必须手动修复它们。

    首先,在正常的数据库迁移中添加Company 模型,然后进行数据迁移并在第一次数据库迁移后运行它,然后再进行一次数据库迁移,从Customer 模型中删除公司字段。

    您可以像往常一样使用manage.py makemigrations 进行数据库迁移,只需在它们之间的迁移文件中添加如下内容,这里我将新公司ForeignKey 字段命名为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),
        ]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-17
      • 2018-11-23
      • 2017-07-11
      • 2015-03-23
      • 1970-01-01
      • 2016-09-29
      • 2020-07-26
      • 1970-01-01
      相关资源
      最近更新 更多