Django import-export导入数据报外键约束失败IntegrityError
你的配置存在3个核心问题,按优先级排序如下:
1. Resource字段映射配置错误
你在resource.py中给外键字段设置的attribute = 'ITHardware.it_hardware_department'是错误的:ModelResource本身已经绑定了ITHardware模型,attribute参数只需要填写模型上的字段名即可,不需要加模型名前缀。这个错误会导致读取到的部门关联值根本不会被赋值到待入库的ITHardware实例上,最终提交时外键字段为无效值,触发FOREIGN KEY constraint failed报错。
2. 缺少厂商外键的映射配置
你第一次导入报ITHardwareManufacturer matching query does not exist,是因为ITHardware模型存在it_hardware_manufacturer外键字段,但你在Resource配置中完全没有添加该字段的映射规则,导入时import-export默认会按主键值匹配关联厂商,而你的导入文件里填的是厂商名称字符串,自然无法匹配到对应数据。
3. 外键字段默认值配置不合理
你给外键字段设置了default = '',外键字段存储的是关联模型的主键值,允许为空时合法空值是None,空字符串会被数据库判定为非法外键值,哪怕设置了db_constraint=False,SQLite等部分数据库仍会强制校验外键合法性,直接抛出约束错误。另外你给部门外键设置的related_name = 'department'逻辑也不合理,related_name是部门模型反向查询关联硬件时用的属性名,设置为hardware_set之类的名称更符合使用习惯。
- 修正模型配置
修改models.py中的外键字段,去掉空字符串默认值,修正反向关联名,改完执行python manage.py makemigrations和python manage.py migrate同步数据库:
class ITHardware(models.Model): it_hardware_model = models.CharField(max_length = 100) it_hardware_serial_number = models.CharField(max_length = 100, blank = True, default = '') it_hardware_department = models.ForeignKey(Department, related_name = 'hardware_set', on_delete = models.SET_NULL, null = True, blank = True, db_constraint=False) # 厂商外键也按同样规则修正,去掉default='' it_hardware_manufacturer = models.ForeignKey(ITHardwareManufacturer, related_name='hardware_set', on_delete=models.SET_NULL, null=True, blank=True, db_constraint=False)
- 修正Resource配置
修改resource.py,修正部门字段的attribute值,补上厂商字段的映射。如果需要导入时自动创建不存在的部门、厂商(不用提前手动在后台录入),可以自定义外键组件实现自动创建逻辑:
from import_export import resources, fields, widgets from .models import ITHardware, Department, ITHardwareManufacturer # 可选:自定义外键组件,导入时遇到不存在的关联数据自动创建 class CreateIfNotExistForeignKeyWidget(widgets.ForeignKeyWidget): def clean(self, value, row=None, *args, **kwargs): if not value: return None instance, _ = self.model.objects.get_or_create(**{self.field: value}) return instance class ITHardwareResource(resources.ModelResource): it_hardware_department = fields.Field( column_name = 'it_hardware_department', attribute = 'it_hardware_department', # 去掉之前错误的ITHardware.前缀 # 不需要自动创建部门就用下面这行 widget = widgets.ForeignKeyWidget(Department, field = 'department_name') # 需要自动创建部门就注释上面一行,用下面的自定义组件 # widget = CreateIfNotExistForeignKeyWidget(Department, field='department_name') ) # 补上厂商字段的映射配置 it_hardware_manufacturer = fields.Field( column_name='it_hardware_manufacturer', attribute='it_hardware_manufacturer', # 不需要自动创建厂商就用下面这行 widget=widgets.ForeignKeyWidget(ITHardwareManufacturer, field='manufacturer') # 需要自动创建厂商就换自定义组件 # widget=CreateIfNotExistForeignKeyWidget(ITHardwareManufacturer, field='manufacturer') ) class Meta(): model = ITHardware fields = ( 'id', 'it_hardware_manufacturer', # 把厂商字段加入导入导出字段列表 'it_hardware_model', 'it_hardware_serial_number', 'it_hardware_department', ) export_order = ( 'id', 'it_hardware_manufacturer', 'it_hardware_model', 'it_hardware_serial_number', 'it_hardware_department', )
- 校验导入文件
确保导入文件的列名和Resource中配置的column_name完全一致,没有多余空格、大小写偏差。
内容的提问来源于stack exchange,提问作者ilayfd

