扩展 django-import-export 的导入表单,为每个导入的行指定固定值 [英] Extend django-import-export's import form to specify fixed value for each imported row

查看:55
本文介绍了扩展 django-import-export 的导入表单,为每个导入的行指定固定值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using django-import-export 1.0.1 with admin integration in Django 2.1.1. I have two models

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')

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

and they can be imported/exported just fine using model resources

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',)

和导入/导出操作可以通过

and import/export actions can be integrated into the admin by

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)

出于 Reasons™,我想更改此设置,以便 Locations 的电子表格 包含 Sector 列可以进口;sector 的值(对于每个导入的行)应该从管理员的 ImportForm 上的一个额外字段中获取.

For Reasons™, I would like to change this set-up so that a spreadsheet of Locations which does not contain a Sector column can be imported; the value of sector (for each imported row) should be taken from an extra field on the ImportForm in the admin.

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

Such a field can indeed be added by overriding import_action on the ModelAdmin as described in Extending the admin import form for django import_export. The next step, to use this value for all imported rows, is missing there, and I have not been able to figure out how to do it.

推荐答案

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

EDIT(2): Solved through the use of sessions. Having a get_confirm_import_form hook would still really help here, but even better would be having the existing ConfirmImportForm carry across all the submitted fields & values from the initial import form.

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

I'm sorry, I thought I had this nailed, but my own code wasn't working as well as I thought it was. This doesn't solve the problem of passing along the sector form field in the ConfirmImportForm, which is necessary for the import to complete. Currently looking for a solution which doesn't involve pasting the whole of import_action() into an ImportMixin subclass. Having a get_confirm_import_form() hook would help a lot here.

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

Still working on a solution for myself, and when I have one I'll update this too.

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

Don't override import_action. It's a big complicated method that you don't want to replicate. More importantly, as I discovered today: there are easier ways of doing this.

首先(正如你提到的),为 Location 创建一个自定义导入表单,允许用户选择一个 Sector:

First (as you mentioned), make a custom import form for Location that allows the user to choose a Sector:

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

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

In the Resource API, there's a before_import_row() hook that is called once per row. So, implement that in your LocationResource class, and use it to add the Sector column:

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 值从导入表单传送到导入确认屏幕.如果您不使用会话,您我需要找到另一种方法来做到这一点.)

(Note: This code uses Django sessions to carry the sector value from the import form to the import confirmation screen. If you're not using sessions, you'll need to find another way to do it.)

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

This is all you need to get the extra data in, and it works for both the dry-run preview and the actual import.

注意 self.request 不存在于默认的 ModelResource - 我们必须通过给 LocationResource 一个自定义构造函数来安装它:

Note that self.request doesn't exist in the default ModelResource - we have to install it by giving LocationResource a custom constructor:

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

(不要担心 self.request 会一直存在.每个 LocationResource 实例不会持续超过一个请求.)

(Don't worry about self.request sticking around. Each LocationResource instance doesn't persist beyond a single request.)

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

The request isn't usually passed to the ModelResource constructor, so we need to add it to the kwargs dict for that call. Fortunately, Django Import/Export has a dedicated hook for that. Override ImportExportModelAdmin's get_resource_kwargs method in LocationAdmin:

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

这就是你所需要的.

这篇关于扩展 django-import-export 的导入表单,为每个导入的行指定固定值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆