Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/321.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 向现有django模型添加新的唯一字段时的最佳做法_Python_Django_Python 3.x_Database Migration_Django Migrations - Fatal编程技术网

Python 向现有django模型添加新的唯一字段时的最佳做法

Python 向现有django模型添加新的唯一字段时的最佳做法,python,django,python-3.x,database-migration,django-migrations,Python,Django,Python 3.x,Database Migration,Django Migrations,我有一个现有的模型,看起来有点像下面 class Resource(models.Model): id = models.AutoField(primary_key=True) 我们已经使用它一段时间了,现在我们的数据库中有大约100万个资源对象(以及相关的ForeignKey/else用法)的实例 我现在需要在这个模型上跟踪另一个ID,我想强制执行的ID是唯一的 other_id = models.IntegerField(unique=True) 此other_id信息当前存储

我有一个现有的模型,看起来有点像下面

class Resource(models.Model):

    id = models.AutoField(primary_key=True)
我们已经使用它一段时间了,现在我们的数据库中有大约100万个
资源
对象(以及相关的ForeignKey/else用法)的实例

我现在需要在这个模型上跟踪另一个ID,我想强制执行的ID是唯一的

other_id = models.IntegerField(unique=True)
other_id
信息当前存储在一些外部CSV中,我希望(在过程中的某个时刻)将此信息加载到所有现有
Resource
实例中

添加上述字段后,Django的
makemigrations
工作正常。但是,当我对现有数据库应用所述迁移时,我得到一个错误,指示我需要为所有现有
资源
实例提供一个默认值。我相信你们很多人都见过类似的事情

绕过这一限制的最佳方法是什么?我想到的一些方法

    • 删除
      unique=True
      要求
    • 应用迁移
    • 从外部将
      other_id
      值加载到所有现有模型(通过一些管理命令或1-off脚本)
    • unique=True
      添加回并应用迁移
    • 将所有现有数据转储到JSON
    • 冲洗所有桌子
    • 应用迁移(使用unique=True)
    • 编写一个脚本,将数据加载回,添加正确的
      other\u id
  • (不确定这是否可行)-编写一些自定义迁移逻辑,以便在运行
    manage.py migrate
    时自动引用这些外部CSV以加载
    其他\u id
    值。如果(在将来的某个时候)有人重新运行这些迁移,而此部分失败(在CSV中找不到现有资源
    id
    以拉出
    其他\u id
    ),则可能会出现问题
  • 所有这些感觉都很复杂,但我想我要做的也不是最简单的事情

    有什么想法吗?我不得不想象过去有人不得不围绕类似的问题工作


    谢谢

    实际上,源或您的问题本身并不是唯一约束,而是您的字段不允许空值且没有默认值这一事实-对于非唯一字段,您也会遇到同样的错误

    这里正确的解决方案是允许字段为null(
    null=True
    ),并将其默认为
    None
    (这将转换为sql“null”)。由于
    null
    值被排除在唯一约束之外(至少在您的db供应商遵守SQL标准的情况下),这允许您应用架构更改,同时仍然确保非null值不能重复


    然后,您可能需要数据迁移来加载已知的“other_id”值,最后是第三次架构迁移,以禁止此字段的空值—如果且仅当您知道已为所有记录填充了此字段。

    Django有一个名为的东西,您可以在应用迁移时创建一个迁移文件来修改/删除/向数据库添加数据

    在这种情况下,您将创建3种不同的迁移:

  • 创建允许使用
    null=True
    的空值的迁移
  • 创建填充数据的数据迁移
  • 通过删除步骤1中添加的
    null=True
    来创建不允许空值的迁移
  • 然后运行
    python manage.py migrate
    时,它将按照正确的顺序应用步骤1-3中的所有迁移

    您的数据迁移如下所示:

    from django.db import migrations
    
    def populate_reference(apps, schema_editor):
        MyModel = apps.get_model('yourappname', 'MyModel')
        for obj in MyModel.objects.all():
            obj.other_id = random_id_generator()
            obj.save()
    
    class Migration(migrations.Migration):
    
        dependencies = [
            ('yourappname', '0001_initial'),
        ]
    
        operations = [
            migrations.RunPython(populate_reference),
        ]
    

    您可以使用
    /manage.py makemigrations--empty yourappname
    命令创建一个空迁移文件。

    感谢您的想法,并指出了“必须提供默认值”的实际原因。看起来很像我的#2,只是我不知道数据迁移是一件事!这与您的第二个解决方案大不相同—您不必导出数据、刷新表,然后编辑和重新导入数据—所有这些都会在大型数据库上花费一些时间,而且最容易出错。实际上,这更接近您的#1,但更安全,因为它确保您不会有非空的重复值(这将打破第二次迁移加回唯一约束)。我是说#2吗?我的意思是#1,除了我把unique和null混淆了:)再次感谢Hanks的回答!使用数据迁移的唯一问题是,我需要依赖一些外部CSV来进行索引(
    resource.other\u id=external\u data[resource.id]
    )。我们的流程一直在创建新的
    资源
    实例,因此,我不能将CSV的“当前状态”复制到django应用程序中,并期望它在将来的任何迁移中都能正常工作。完成后,您可以放弃这些步骤,只为将来的迁移创建模型的最终状态。@wKavey,如果您不能可靠地在新的
    Resource
    创建后,您需要将字段保持为空(且无默认值),并在“其他id”可用时设置一些自动更新资源的方法(根据课程的上下文,这是正确的解决方案)。@Brunodesshuilliers感谢您跟随我来到这里。目前,
    other_id
    的值保存在一个CSV文件中,该文件将
    id
    映射到
    other_id
    。每当在数据库中创建新资源时,都会在此CSV中添加一个新条目。如果我正在编写一个利用此CSV填充
    其他id
    的数据迁移,那么如果我确定所有资源及其
    id
    都已表示,就可以了。我的问题是,我需要检查迁移到版本控制中的情况,大概是和CSV一起。如果将来有其他人运行此迁移,则无法保证