扩展 django-import-export 的导入表单,为每个导入的行指定固定值



我正在使用django-import-export 1.0.1和Django 2.1.1中的管理员集成。我有两个模型

from django.db import models
class Sector(models.Model):
code = models.CharField(max_length=30, primary_key=True)
class Location(models.Model):
code = models.CharField(max_length=30, primary_key=True)
sector = ForeignKey(Sector, on_delete=models.CASCADE, related_name='locations')

并且可以使用模型资源很好地导入/导出它们

from import_export import resources
from import_export.fields import Field
from import_export.widgets import ForeignKeyWidget
class SectorResource(resources.ModelResource):
code = Field(attribute='code', column_name='Sector')
class Meta:
model = Sector
import_id_fields = ('code',)
class LocationResource(resources.ModelResource):
code = Field(attribute='code', column_name='Location')
sector = Field(attribute='sector', column_name='Sector',
widget=ForeignKeyWidget(Sector, 'code'))
class Meta:
model = Location
import_id_fields = ('code',)

导入/导出操作可以通过以下方式集成到管理中

from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
class SectorAdmin(ImportExportModelAdmin):
resource_class = SectorResource
class LocationAdmin(ImportExportModelAdmin):
resource_class = LocationResource
admin.site.register(Sector, SectorAdmin)
admin.site.register(Location, LocationAdmin)

出于原因™,我想更改此设置,以便可以导入不包含扇区列的Locations电子表格;sector的值(对于每个导入的行)应取自管理员中ImportForm上的额外字段。

确实可以通过覆盖ModelAdmin上的import_action来添加这样的字段,如扩展 django import_export 的管理导入表单中所述。下一步,对所有导入的行使用此值,那里缺少,我无法弄清楚如何做到这一点。

EDIT(2):通过使用会话来解决。有一个get_confirm_import_form钩子在这里仍然会很有帮助,但更好的是让现有的ConfirmImportForm从初始导入表单中携带所有提交的字段和值。

编辑:对不起,我以为我已经钉好了,但我自己的代码没有我想象的那么好。这并不能解决在ConfirmImportForm中传递sector表单字段的问题,这是完成导入所必需的。目前正在寻找一种不涉及将整个import_action()粘贴到ImportMixin子类中的解决方案。在这里有一个get_confirm_import_form()钩会有很大帮助。

仍在为自己制定解决方案,当我有一个解决方案时,我也会更新它。


不要覆盖import_action.这是一个您不想复制的复杂方法。更重要的是,正如我今天发现的那样:有更简单的方法可以做到这一点。

首先(如您所提到的),为Location制作一个自定义导入表单,允许用户选择Sector

class LocationImportForm(ImportForm):
sector = forms.ModelChoiceField(required=True, queryset=Sector.objects.all())

在资源 API 中,有一个每行调用一次的before_import_row()挂钩。因此,在您的LocationResource类中实现它,并使用它来添加Sector列:

def before_import_row(self, row, **kwargs):
sector = self.request.POST.get('sector', None)
if contract:
self.request.session['import_context_sector'] = sector
else:
# if this raises a KeyError, we want to know about it.
# It means that we got to a point of importing data without
# contract context, and we don't want to continue.
try:
sector = self.request.session['import_context_sector']
except KeyError as e:
raise Exception("Sector context failure on row import, " +
f"check resources.py for more info: {e}")
row['sector'] = sector

(注意:此代码使用 Django 会话将sector值从导入表单传递到导入确认屏幕。如果您不使用会话,则需要找到另一种方法来执行此操作。

这就是获取额外数据所需的全部内容,它适用于试运行预览和实际导入。

请注意,默认ModelResource中不存在self.request- 我们必须通过LocationResource自定义构造函数来安装它:

def __init__(self, request=None):
super()
self.request = request

(不用担心self.request留下来。每个LocationResource实例不会保留超过单个请求。

request通常不会传递给ModelResource构造函数,因此我们需要将其添加到该调用的 kwargs 字典中。幸运的是,Django Import/Export 有一个专用的钩子。在LocationAdmin中覆盖ImportExportModelAdminget_resource_kwargs方法:

def get_resource_kwargs(self, request, *args, **kwargs):
rk = super().get_resource_kwargs(request, *args, **kwargs)
rk['request'] = request
return rk

这就是你所需要的。

最新更新