Django import_export导入CSV遇多类异常:IntegrityError等问题求助
Django CSV导入导出问题解决指南
问题场景
Django应用实现了CSV导出/导入功能:
- 导出:通过自定义
GreffonResource类实现自定义表头,外键字段显示名称而非ID,功能正常。 - 导入:修改CSV后出现多类错误:
- 初始报错 Integrity Error: (1048, "Column 'produit_id' cannot be null"),确认是编辑器在文件末尾添加空行导致。
- 新增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)
问题根源
- import_id_fields配置错误:同时将
id和produit设为导入标识字段,导致系统尝试用两者组合定位Greffons实例,与“基于Greffons自身ID匹配”的需求冲突。 - 外键显示字段配置冲突:
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)
关键调整说明
- 标记外键显示字段为只读:给
variete、espece、portegreffe添加readonly=True,确保导入时忽略这些字段,避免匹配逻辑冲突。 - 修正导入标识字段:
import_id_fields仅保留id,确保系统通过Greffons自身ID定位实例,解决多对象返回错误。 - 保留外键ID匹配逻辑:
produit字段的ForeignKeyWidget仍基于id匹配,满足导入时通过Produit ID关联外键的需求。 - 过滤空行:导入前过滤CSV中的空行,从根源避免字段为空的完整性错误。
内容的提问来源于stack exchange,提问作者Odissine
相关产品推荐
相关产品推荐

