我正在使用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
中覆盖ImportExportModelAdmin
的get_resource_kwargs
方法:
def get_resource_kwargs(self, request, *args, **kwargs):
rk = super().get_resource_kwargs(request, *args, **kwargs)
rk['request'] = request
return rk
这就是你所需要的。