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

django-import-export含delete字段导入更新时报错求助

问题

使用django-import-export导入含delete字段的文件时,触发错误:

BaseBoqItem has no field named 'delete'

明确模型不需要delete字段,此设置并非失误。为实现用户标记行后导入自动删除对应数据,已按django-import-export官方文档配置,但仍触发该错误。

  • 导入未修改的导出文件时正常(推测因skip_unchanged=True)
  • 仅标记delete为1时导入可成功删除实例
  • 修改其他字段并导入时触发上述错误

相关代码

模型代码

class BaseBoqItem(models.Model):
    lot = models.ForeignKey(BaseLot, on_delete=models.PROTECT, related_name='boq_items')

    code = models.CharField(max_length=20, null=True)
    name_tm = models.CharField(max_length=255)
    name_ru = models.CharField(max_length=255, null=True)
    name_en = models.CharField(max_length=255, null=True)
    name_original = models.CharField(max_length=255, null=True)
    quantity = models.DecimalField(max_digits=12, decimal_places=2)
    unit = models.ForeignKey(BaseUnit, on_delete=models.PROTECT)
    material_unit_price = models.DecimalField(max_digits=12, decimal_places=2)
    labor_unit_price = models.DecimalField(max_digits=12, decimal_places=2)
    transport_unit_price = models.DecimalField(max_digits=12, decimal_places=2, null=True)
    
    def __str__(self) -> str:
        return f'{self.code} - {self.name_tm}'

Resource代码

class BaseBoqItemResource(resources.ModelResource):
    lot = fields.Field(
        column_name='lot', attribute='lot', 
        widget=ForeignKeyWidget(BaseLot, field='code'))
    unit = fields.Field(
        column_name='unit', attribute='unit', 
        widget=ForeignKeyWidget(BaseUnit, field='code_tm'))
    delete = fields.Field(readonly=True)
    def for_delete(self, row, instance):
        return self.fields['delete'].clean(row)
    
    class Meta:
        model = BaseBoqItem
        skip_unchanged = True
        use_bulk = True
        fields = (
            'id', 'lot', 'code', 
            'name_tm', 'name_ru', 'name_en', 'name_original', 
            'quantity', 'unit', 
            'material_unit_price', 'labor_unit_price', 'transport_unit_price'
        )
        exclude=('delete', )
        export_order = (
            'id', 'lot', 'code', 
            'name_tm', 'name_ru', 'name_en', 'name_original', 
            'quantity', 'unit', 
            'material_unit_price', 'labor_unit_price', 'transport_unit_price', 
            'delete'
        )

视图代码

class BaseBoqItemModelViewset(ModelViewSet):
    queryset = BaseBoqItem.objects.all()
    serializer_class = BaseBoqItemModelSerializer
    filterset_class = BaseBoqItemFilterSet
    pagination_class = CustomPagination

    @action(detail=False, methods=['get'])
    def exporttoexcel(self, request, *args, **kwargs):
        base_boq_item_resource = BaseBoqItemResource()
        dataset = base_boq_item_resource.export()
        response = HttpResponse(dataset.xlsx, content_type='application/vnd.ms-excel')
        response['Content-Disposition'] = 'attachment; filename="base_boq_items.xlsx"'
        return response
    
    @csrf_exempt
    @action(detail=False, methods=['post'])
    def importfromexcel(self, request, *args, **kwargs):
        excel = request.FILES['excel']
        dataset = Dataset().load(excel)
        base_boq_item_resource = BaseBoqItemResource()

        result = base_boq_item_resource.import_data(dataset, dry_run=True, raise_exception=True)
        if not result.has_errors():
            base_boq_item_resource.import_data(dataset, dry_run=False, raise_errors=True)
            return Response({'status': 'Imported successfully'})
        return Response({'status': 'Error when importing'}, status=status.HTTP_400_BAD_REQUEST)

堆栈信息

System check identified no issues (0 silenced).
January 19, 2024 - 15:09:34
Django version 4.2.8, using settings 'core.settings' 
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

Internal Server Error: /api/v1/baseboqitems/importfromexcel/
Traceback (most recent call last):
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\django\db\models\options.py", line 681, in get_field
    return self.fields_map[field_name]
KeyError: 'delete'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\django\core\handlers\exception.py", line 55, in inner
    response = get_response(request)
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\django\views\decorators\csrf.py", line 56, in wrapper_view
    return view_func(*args, **kwargs)
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\rest_framework\viewsets.py", line 125, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\rest_framework\views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
    raise exc
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\rest_framework\views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\django\views\decorators\csrf.py", line 56, in wrapper_view
    return view_func(*args, **kwargs)
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\common\views.py", line 280, in importfromexcel
    result = base_boq_item_resource.import_data(dataset, dry_run=True, raise_exception=True)
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\import_export\resources.py", line 896, in import_data
    result = self.import_data_inner(
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\import_export\resources.py", line 1020, in import_data_inner
    self.bulk_update(
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\import_export\resources.py", line 452, in bulk_update
    self.handle_import_error(result, e, raise_errors)
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\import_export\resources.py", line 446, in bulk_update
    self._meta.model.objects.bulk_update(
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\django\db\models\manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\django\db\models\query.py", line 854, in bulk_update
    fields = [self.model._meta.get_field(name) for name in fields]
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\django\db\models\query.py", line 854, in <listcomp>
    fields = [self.model._meta.get_field(name) for name in fields]
  File "C:\Users\a.soyunjaliyev\Documents\coding\smetcik\backend\venv\lib\site-packages\django\db\models\options.py", line 683, in get_field
    raise FieldDoesNotExist(
django.core.exceptions.FieldDoesNotExist: BaseBoqItem has no field named 'delete'
[19/Jan/2024 15:09:38] "POST /api/v1/baseboqitems/importfromexcel/ HTTP/1.1" 500 149979

使用版本:django-import-export==3.3.4

请问遗漏了什么配置?如何解决该错误?


解决方案

问题原因

开启use_bulk=True时,django-import-export会尝试批量更新所有变更字段,但当前配置中手动定义的delete字段仍会被视为需要处理的字段(即使标记了exclude=('delete',)),批量更新时会试图将delete字段传递给Django的bulk_update方法,而模型中并没有这个字段,从而触发错误。

仅标记delete为1时,流程进入删除逻辑而非更新逻辑,所以不会触发批量更新错误;未修改的文件因为skip_unchanged=True跳过了更新,也不会报错。

修改方法

调整Resource配置,确保delete字段不参与导入的更新操作:

class BaseBoqItemResource(resources.ModelResource):
    lot = fields.Field(
        column_name='lot', attribute='lot', 
        widget=ForeignKeyWidget(BaseLot, field='code'))
    unit = fields.Field(
        column_name='unit', attribute='unit', 
        widget=ForeignKeyWidget(BaseUnit, field='code_tm'))
    # 添加import_id_fields=False,明确该字段不参与导入字段映射
    delete = fields.Field(readonly=True, import_id_fields=False)
    
    def for_delete(self, row, instance):
        # 直接读取行数据中的delete标记,无需通过clean方法
        return row.get('delete') in ('1', 1)
    
    class Meta:
        model = BaseBoqItem
        skip_unchanged = True
        use_bulk = True
        fields = (
            'id', 'lot', 'code', 
            'name_tm', 'name_ru', 'name_en', 'name_original', 
            'quantity', 'unit', 
            'material_unit_price', 'labor_unit_price', 'transport_unit_price'
        )
        exclude=('delete', )
        export_order = (
            'id', 'lot', 'code', 
            'name_tm', 'name_ru', 'name_en', 'name_original', 
            'quantity', 'unit', 
            'material_unit_price', 'labor_unit_price', 'transport_unit_price', 
            'delete'
        )

解释

  • import_id_fields=False告诉django-import-export,该字段不需要映射到模型字段,也不会参与导入时的更新操作
  • 直接读取row中的值判断删除标记,避免调用clean方法带来的额外处理,同时确保逻辑准确

如果不介意性能损耗,也可以临时关闭use_bulk=True(改为use_bulk=False),但批量更新的效率优势会丢失,不推荐。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 07:04:54