多次导入同一文件时,Django 导入导出重复行

时间:2021-03-11 19:08:33

标签: django django-import-export

我正在构建一个工具,允许用户上传 CSV 文件以使用 django-import-export 工具更新数据库。我用一个数据行上传了我的测试 CSV 文件,然后再次上传它并得到一个重复的行(使用新的主键,但所有其他值都相同)。 row.import_type 值是“已更新”,但唯一更新的是 ID。

然后我第三次上传同一个文件并得到一个错误:

app.models.Role.MultipleObjectsReturned: get() returned more than one Role -- it returned 2!

(顺便说一句,我真的很感谢那个错误消息中的感叹号。)

理想情况下,我会在文件的第二次导入和第三次导入时跳过一行。我想我可以接受错误。文件内容为:

    Sales Role,System Role,System Plan,id
    Sales Rep,Account Executive,951M-NA,

这是用户在导出 csv 数据集时获得的格式。理想情况下,他们会导出一个文件,更改几列(除了 import_id_field 的名称),然后重新上传数据。

在 app/resources.py 中:

    class RoleResourec(resources.ModelResource):
        name = Field(attribute='name', column_name="Sales Role")
        default_role = Field(attribute='default_role', column_name="System Role")
        default_plan = Field(attribute='default_plan', column_name="System Plan")
    
        class Meta:
            models=Role
            fields= ('id', 'name', 'default_role', 'default_plan')
            import_id_fields = ('name',)
            skip_unchanged = True

据我所知,在第二次导入时,get_or_init_instance() 方法没有从第一次导入中找到对象,但在第三次导入时确实找到了它们。我没有对资源执行任何操作来自定义导入工作流程,如 Import data workflow 页面中所述。

这里出了什么问题?我是否需要自定义导入工作流程,还是我错过了资源中的另一个必需属性?

2 个答案:

答案 0 :(得分:1)

只有在导入的行和持久化对象中所有声明的字段都相同时,逻辑才会跳过该行。如果有任何字段不同,则将执行更新。

为此,您在 import_id_fields 中声明的字段必须是一行的唯一匹配项,否则您将获得 MultipleObjectsReturned

在您的情况下,如果正在创建重复的行,那么它必须意味着 name 在第二次运行时不存在于数据库中。我假设您没有覆盖 ModelInstanceLoader 或正在以批量模式运行,因为这会破坏跳过行逻辑。

默认情况下,import_id_fields 设置为行 ID,因此如果您可以将其包含在导出中,那么您就可以保证拥有唯一的行。显然用户不应该改变这个字段,否则你会得到重复。

MultipleObjectsReturned 错误来自 here,它只是对 Role.objects.get(name=<n>) 的调用。

答案 1 :(得分:0)

这是一个非常简单的解决方案:


    class RoleResourec(resources.ModelResource):
            name = Field(attribute='name', column_name="Sales Role")
            default_role = Field(attribute='default_role', column_name="System Role")
            default_plan = Field(attribute='default_plan', column_name="System Plan")
        
            class Meta:
                models=Role
                fields= ('name', 'default_role', 'default_plan')
                import_id_fields = ('name',)
                skip_unchanged = True

我所要做的就是从 'id' 类的字段列表中删除 Meta,现在我得到了预期的行为。

我可以将此文件导出为 CSV(并且 id 列不会出现),编辑列表,然后重新上传,系统会跳过和更新,甚至根据需要添加新内容。