You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

django-import-export导入外键数据报get()返回多个Block错误排查

报错信息

向带外键的模型/表导入数据时,部分行导入失败抛出如下错误:

Line number: 1 - get() returned more than one Block -- it returned 2!
, HARYANA, 11, FATEHABAD, 195, FATEHABAD, 641, AHLISADAR, 57891
Traceback (most recent call last):
File "/home/webtesting/django_venv/lib/python3.9/site-packages/import_export/resources.py", line 660, in import_row
self.before_import_row(row, **kwargs)
File "/home/webtesting/Excel/DAMU_Excel_Upload_AMFU_DAMU/damuwhatsapp/resources.py", line 89, in before_import_row
(cat, _created) = Block.objects.get_or_create(block_name=block_name)
File "/home/webtesting/django_venv/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/webtesting/django_venv/lib/python3.9/site-packages/django/db/models/query.py", line 657, in get_or_create
return self.get(**kwargs), False
File "/home/webtesting/django_venv/lib/python3.9/site-packages/django/db/models/query.py", line 499, in get
raise self.model.MultipleObjectsReturned(
damuwhatsapp.models.Block.MultipleObjectsReturned: get() returned more than one Block -- it returned 2!
问题原因

报错核心是Django ORM的get()方法要求查询结果必须有且仅有1条匹配记录,但当前查询匹配到了2条及以上的Block记录。
相同结构的数据部分导入成功、部分失败的逻辑非常明确:

  • 导入成功的行对应的block_name,在Block表中要么只有1条匹配记录,要么完全不存在(get_or_create会自动创建新记录)
  • 导入失败的行对应的block_name,在Block表中已经存在至少2条block_name值完全相同的重复记录,直接触发MultipleObjectsReturned异常

现有代码存在两个设计缺陷放大了这个问题:

  1. Block模型的block_name字段没有加唯一约束,历史数据中已经产生了重名记录,且后续新增数据时也没有机制阻止重名Block生成
  2. 已经通过ForeignKeyWidget配置了外键关联逻辑,又在before_import_row钩子中冗余写了get_or_create查询逻辑,提前触发了重复数据的报错,且钩子中给row['block_name']赋值名称字符串的操作完全多余,后续ForeignKeyWidget还是会重新做一次查询。
修复方案

按以下步骤操作即可彻底解决问题:

  • 第一步:清理Block表现有重复数据
    先执行脚本找出所有重名的Block记录,手动合并、删除重复项,保证每个block_name仅对应1条Block记录:

    from django.db.models import Count
    # 查询所有重名的block_name
    duplicate_block_names = Block.objects.values('block_name')\
        .annotate(record_count=Count('id'))\
        .filter(record_count__gt=1)
    print(duplicate_block_names)
    

    清理时注意检查是否存在值前后带空格、大小写不一致的“隐形重复”(比如FATEHABAD 和FATEHABAD视觉上无差异,但实际是不同字符串),可统一做去空格处理。

  • 第二步:给Block表加唯一约束,从根源避免重复数据
    修改Block模型的block_name字段,添加unique=True参数:

    class Block(models.Model):
        # 其余原有字段保持不变
        block_name = models.CharField(max_length=自定义字段长度, unique=True)
    

    保存后依次执行python manage.py makemigrations、python manage.py migrate命令,将约束应用到数据库。

  • 第三步:简化导入逻辑,删除冗余代码
    直接删除VillageResource中整个before_import_row方法,ForeignKeyWidget本身已经实现了按block_name查询关联Block实例的逻辑,不需要手动在钩子中重复查询。
    如果需要实现“导入时遇到不存在的Block自动创建”的能力,自定义支持自动创建的ForeignKeyWidget替换原有组件即可,不要在导入钩子中单独写查询逻辑:

    from import_export.widgets import ForeignKeyWidget
    
    class AutoCreateForeignKeyWidget(ForeignKeyWidget):
        def clean(self, value, row=None, *args, **kwargs):
            # 先查询关联记录
            instance = super().clean(value, row, *args, **kwargs)
            # 不存在则自动创建
            if not instance:
                instance = self.model.objects.create(**{self.field: value})
            return instance
    
    # 替换原有block_name字段的widget配置
    block_name = fields.Field(
        column_name='block_name',
        attribute='block_name',
        widget=AutoCreateForeignKeyWidget(Block, 'block_name')
    )
    

内容的提问来源于stack exchange,提问作者Abhilash Singh Chauhan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 19:15:47