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

Django import_export导入CSV遇多类异常:IntegrityError等问题求助

Django CSV导入导出问题解决指南

问题场景

Django应用实现了CSV导出/导入功能:

  • 导出:通过自定义GreffonResource类实现自定义表头,外键字段显示名称而非ID,功能正常。
  • 导入:修改CSV后出现多类错误:
    1. 初始报错 Integrity Error: (1048, "Column 'produit_id' cannot be null"),确认是编辑器在文件末尾添加空行导致。
    2. 新增Produit相关字段后,导出正常,但导入出现两类错误:
      • 保留produit在import_id_fields中时:DoesNotExist at /produits/import_xls/ Produit matching query does not exist.
      • 移除produit后:MultipleObjectsReturned at /produits/import_xls/ get() returned more than one Greffons -- it returned 2!

已确认CSV中ID列值唯一,需求为:导出包含ID和外键文本值的文件,导入时基于produit的ID关联外键。

相关代码

原GreffonResource类

class GreffonResource(resources.ModelResource):
    comm = fields.Field(
        column_name='Pre-Commandes',
        attribute='comm',
    )
    greffons = fields.Field(
        column_name='Greffons',
        attribute='greffons',
    )
    objectif = fields.Field(
        column_name='Objectifs',
        attribute='objectif',
    )
    realise = fields.Field(
        column_name='Realises',
        attribute='realise',
    )
    reussi = fields.Field(
        column_name='Reussis',
        attribute='reussi',
    )
    variete = fields.Field(
        column_name='Variete',
        attribute='produit__variete',
        widget=widgets.ForeignKeyWidget(Produit, 'nom')
    )
    espece = fields.Field(
        column_name='Espece',
        attribute='produit__espece',
        widget=widgets.ForeignKeyWidget(Produit, 'nom')
    )
    portegreffe = fields.Field(
        column_name='Porte Greffe',
        attribute='produit__portegreffe',
        widget=widgets.ForeignKeyWidget(Produit, 'nom')
    )
    produit = fields.Field(
        column_name='Produits',
        attribute='produit',
        widget=widgets.ForeignKeyWidget(Produit, 'id')
    )
    rang = fields.Field(
        column_name='Rangs',
        attribute='rang',
    )
    class Meta:
        model = Greffons
        fields = ('id', 'produit', 'variete', 'espece', 'portegreffe', 'comm', 'greffons', 'objectif', 'realise', 'reussi', 'rang')
        export_order = ['id', 'produit', 'variete', 'espece', 'portegreffe', 'comm', 'greffons', 'objectif', 'realise', 'reussi', 'rang']
        import_id_fields = ('id', 'produit')

原导入逻辑

produit_resource = GreffonResource()
dataset = Dataset()
new_datas = request.FILES['myfile']
imported_data = dataset.load(new_datas.read().decode(), format='csv')
if categorie == "GREFFONS":
    result = produit_resource.import_data(imported_data, dry_run=True, raise_errors=True)  

问题根源

  1. import_id_fields配置错误:同时将id和produit设为导入标识字段,导致系统尝试用两者组合定位Greffons实例,与“基于Greffons自身ID匹配”的需求冲突。
  2. 外键显示字段配置冲突:variete、espece、portegreffe字段使用produit__xxx属性的同时,指定ForeignKeyWidget(Produit, 'nom'),导入时系统会尝试用nom匹配Produit,而非预期的ID。

修正方案

调整后的GreffonResource类

from import_export import resources, fields
from import_export.widgets import ForeignKeyWidget
from .models import Greffons, Produit

class GreffonResource(resources.ModelResource):
    # 常规字段自定义表头
    comm = fields.Field(column_name='Pre-Commandes', attribute='comm')
    greffons = fields.Field(column_name='Greffons', attribute='greffons')
    objectif = fields.Field(column_name='Objectifs', attribute='objectif')
    realise = fields.Field(column_name='Realises', attribute='realise')
    reussi = fields.Field(column_name='Reussis', attribute='reussi')
    rang = fields.Field(column_name='Rangs', attribute='rang')
    
    # 外键文本显示字段(仅用于导出,导入忽略)
    variete = fields.Field(
        column_name='Variete',
        attribute='produit__variete',
        readonly=True
    )
    espece = fields.Field(
        column_name='Espece',
        attribute='produit__espece',
        readonly=True
    )
    portegreffe = fields.Field(
        column_name='Porte Greffe',
        attribute='produit__portegreffe',
        readonly=True
    )
    
    # 外键关联字段(导入时基于ID匹配)
    produit = fields.Field(
        column_name='Produits',
        attribute='produit',
        widget=ForeignKeyWidget(Produit, 'id')
    )

    class Meta:
        model = Greffons
        fields = ('id', 'produit', 'variete', 'espece', 'portegreffe', 'comm', 'greffons', 'objectif', 'realise', 'reussi', 'rang')
        export_order = ['id', 'produit', 'variete', 'espece', 'portegreffe', 'comm', 'greffons', 'objectif', 'realise', 'reussi', 'rang']
        # 仅用Greffons自身ID作为导入匹配标识
        import_id_fields = ('id',)
        # 跳过未修改的记录,优化导入性能
        skip_unchanged = True
        report_skipped = False

导入逻辑优化(过滤空行)

produit_resource = GreffonResource()
dataset = Dataset()
new_datas = request.FILES['myfile']
# 读取内容并过滤空行,避免空行导致的字段为空错误
content = new_datas.read().decode()
filtered_content = '\n'.join([line for line in content.split('\n') if line.strip()])
imported_data = dataset.load(filtered_content, format='csv')
if categorie == "GREFFONS":
    result = produit_resource.import_data(imported_data, dry_run=True, raise_errors=True)

关键调整说明

  1. 标记外键显示字段为只读:给variete、espece、portegreffe添加readonly=True,确保导入时忽略这些字段,避免匹配逻辑冲突。
  2. 修正导入标识字段:import_id_fields仅保留id,确保系统通过Greffons自身ID定位实例,解决多对象返回错误。
  3. 保留外键ID匹配逻辑:produit字段的ForeignKeyWidget仍基于id匹配,满足导入时通过Produit ID关联外键的需求。
  4. 过滤空行:导入前过滤CSV中的空行,从根源避免字段为空的完整性错误。

内容的提问来源于stack exchange,提问作者Odissine

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 18:40:38