管理员集成

导入导出功能的主要特点之一是支持与Django Admin站点的集成。这为导入和导出Django对象提供了一个便捷的界面。有关如何启用和配置admin站点的详细信息,请参阅`Django Admin文档<https://docs.djangoproject.com/en/stable/ref/contrib/admin/>`_。

你也可以安装并运行 示例应用 来熟悉 Admin 集成。

将进出口与您的应用程序集成需要额外配置。

管理员集成通过继承 ImportExportModelAdmin 或可用的混入类之一(ImportMixinExportMixinImportExportMixin)实现:

# app/admin.py
from django.contrib import admin
from .models import Book
from import_export.admin import ImportExportModelAdmin

@admin.register(Book)
class BookAdmin(ImportExportModelAdmin):
    resource_classes = [BookResource]

一旦此配置存在(并且服务器已重启),将向用户显示“import”和“export”按钮。点击每个按钮将打开一个工作流程,用户可以选择导入或导出的类型。

您可以将多个资源分配给 resource_classes 属性。这些资源将在UI中以选择下拉框的形式呈现。

_images/django-import-export-change.png

带有导入和导出按钮的变更视图的屏幕截图。

导入

要启用导入功能,请子类化 ImportExportModelAdmin 或使用其中一个可用的 mixin,例如 ImportMixinImportExportMixin

启用导入功能意味着UI按钮将自动显示在Admin页面上:

导入按钮

点击后,用户将被引导至导入工作流程。默认情况下,导入为两步流程,但也可配置为单步流程(参见 IMPORT_EXPORT_SKIP_ADMIN_CONFIRM)。

两步流程是:

  1. 选择要导入的文件和格式。

  2. 预览导入数据并确认导入。

“导入”视图的屏幕截图

“导入”视图的屏幕截图。

“确认导入”视图的屏幕截图

“确认导入”视图的截图。

导入确认

为支持导入确认,上传的数据在步骤1(选择文件)后被写入临时存储,并在步骤2(导入确认)后读取以进行最终导入。

有三种临时存储机制。

  1. 主机服务器上的临时文件存储(默认)。这仅适用于开发环境。不建议在生产站点使用临时文件系统存储。

  2. Django缓存

  3. Django存储 <https://docs.djangoproject.com/en/stable/ref/files/storage/>`_.

要修改所使用的存储机制,请参考设置 IMPORT_EXPORT_TMP_STORAGE_CLASS

临时存储的选择将受以下因素影响:

  • 正在导入的数据的敏感性

  • 上传量和上传频率。

  • 文件上传大小

  • 使用容器或负载均衡服务器。

临时资源在确认步骤后数据成功导入时被移除。

对于敏感数据,您需要确切了解临时文件的存储方式,并确保数据得到适当的安全保护和管理。

警告

如果用户未完成工作流的确认步骤,或在导入过程中出现错误,则临时资源可能不会被删除。这需要在生产环境中理解和管理。例如,使用缓存过期策略或cron作业来清除陈旧资源。

可定制的存储

如果使用 MediaStorage 作为存储模块,那么你可以定义使用哪个存储后端实现来处理持久化数据的创建/读取/删除操作。

如果使用Django 4.2或更高版本,使用`STORAGES <https://docs.djangoproject.com/en/stable/ref/settings/#std-setting-STORAGES>`_设置来定义后端,否则使用:ref:import_export_default_file_storage

你可以提供自定义存储后端的路径,或者使用现有的后端如 django-storages

如果没有提供自定义存储实现,则使用Django默认处理程序。

例如,如果使用django-storages,你可以通过以下方式将s3配置为临时存储位置:

IMPORT_EXPORT_TMP_STORAGE_CLASS = "import_export.tmp_storages.MediaStorage"

STORAGES = {
    "default": {
        "BACKEND": "django.core.files.storage.FileSystemStorage",
    },
    "import_export": {
        "BACKEND": "storages.backends.s3.S3Storage",
        "OPTIONS": {
            "bucket_name": "<your bucket name>",
            "region_name": "<your region>",
            "access_key": "<your key>",
            "secret_key": "<your secret>"
        },
    },
}

如何格式化UI错误消息

Admin UI 导入错误消息可以使用 import_error_display 属性进行格式化。

导出

与导入一样,也可以配置导出功能。

为此,子类化 ImportExportModelAdmin 或使用可用的混入之一,例如 ExportMixin,或 ImportExportMixin

启用导出功能意味着UI按钮将自动显示在Admin页面上:

导出按钮

点击后,用户将被引导进入导出工作流程。

导出是一个两步过程。当点击“export”按钮时,用户将被引导至一个新屏幕,在那里可以选择“resource”、“fields”和“file format”。

导出确认页面

导出“确认”页面。

点击“submit”后,导出文件将自动下载到客户端(通常下载到“Downloads”文件夹)。

可以通过设置 IMPORT_EXPORT_SKIP_ADMIN_EXPORT_UI 标志或设置 skip_export_form 来禁用这一额外步骤。

通过管理员操作导出

可以配置Admin UI,以便用户选择他们想要导出的项目:

选择要导出的项目

要执行此操作,只需声明一个包含 ExportActionMixin 的 Admin 实例:

class BookAdmin(ImportExportModelAdmin, ExportActionMixin):
  # additional config can be supplied if required
  pass

然后注册这个Admin:

admin.site.register(Book, BookAdmin)

请注意,上述示例特指:ref:示例应用<exampleapp>,您需要修改此内容以引用您自己的模型实例。在示例应用中,'Category'模型具备此功能。

点击“Go”选中项目后,用户将被定向至:ref:导出'确认'页面<export_confirm>

可以通过设置 IMPORT_EXPORT_SKIP_ADMIN_ACTION_EXPORT_UIIMPORT_EXPORT_SKIP_ADMIN_EXPORT_UI 标志,或者通过设置 skip_export_form_from_actionskip_export_form 来禁用这个额外步骤。

备注

如果部署到多租户环境,可能需要确保一组用户无法导出属于另一组的数据。为此,需将可导出项的范围过滤为仅限用户应被允许导出的项目。参见 get_export_queryset()

从模型实例更改表单导出

通过管理员操作导出 启用时,也可以从模型实例更改表单导出:

从变更表单导出

从模型实例更改表单导出

点击“导出”时,用户将被定向到:ref:export 'confirm' page<export_confirm>

可以通过设置 show_change_form_export 属性从 UI 中移除此按钮,例如:

class CategoryAdmin(ExportActionModelAdmin):
    show_change_form_export = False

自定义管理员导入表单

可以修改模型管理中使用默认导入表单。例如,要在导入表单中添加额外字段,可以子类化并扩展 ConfirmImportForm,因为导入是一个两步过程)。

要使用您自定义的表单,更改您的``ModelAdmin``类上的相应属性:

例如,假设您想要导入书籍并将每本书设置为具有相同的作者,从下拉菜单中选择。您可以扩展导入表单以包含 author 字段来从中选择作者。

备注

使用:ref:`示例应用程序<exampleapp>`导入电子书演示了这一点。

_images/custom-import-form.png

自定义导入视图的屏幕截图。

自定义表单(例如参见``tests/core/forms.py``):

class CustomImportForm(ImportForm):
    author = forms.ModelChoiceField(
        queryset=Author.objects.all(),
        required=True)

class CustomConfirmImportForm(ConfirmImportForm):
    author = forms.ModelChoiceField(
        queryset=Author.objects.all(),
        required=True)

自定义 ModelAdmin``(例如参见 ``tests/core/admin.py):

class CustomBookAdmin(ImportMixin, admin.ModelAdmin):
    resource_classes = [EBookResource]
    import_form_class = CustomImportForm
    confirm_form_class = CustomConfirmImportForm

    def get_confirm_form_initial(self, request, import_form):
        initial = super().get_confirm_form_initial(request, import_form)

        # Pass on the `author` value from the import form to
        # the confirm form (if provided)
        if import_form:
            initial['author'] = import_form.cleaned_data['author'].id
        return initial

admin.site.register(EBook, CustomBookAdmin)

为了保存所选作者连同EBook,还需要另外几个方法。将以下内容添加到``CustomBookAdmin``类中(位于``tests/core/admin.py``):

def get_import_data_kwargs(self, request, *args, **kwargs):
    """
    Prepare kwargs for import_data.
    """
    form = kwargs.get("form", None)
    if form and hasattr(form, "cleaned_data"):
        kwargs.update({"author": form.cleaned_data.get("author", None)})
    return kwargs

然后在 EBookResource 类(位于 tests/core/admin.py 中)添加以下内容:

def after_init_instance(self, instance, new, row, **kwargs):
    if "author" in kwargs:
        instance.author = kwargs["author"]

所选作者现在设置为实例对象上的一个属性。当实例保存时,作者将作为外键关系与实例关联。

进一步自定义

要进一步自定义导入表单,您可能需要考虑重写以下 ImportMixin 方法:

然后可以从 Resource 方法中读取参数,例如:

参见

管理员

可用的混入和选项。

自定义管理员导出表单

也可以向导出表单添加字段,以便对导出数据进行筛选。例如,我们可以按Author筛选导出数据。

_images/custom-export-form.png

自定义导出视图的屏幕截图。

自定义表单(例如参见``tests/core/forms.py``):

class CustomExportForm(AuthorFormMixin, ExportForm):
    """Customized ExportForm, with author field required."""
    author = forms.ModelChoiceField(
        queryset=Author.objects.all(),
        required=True)

自定义 ModelAdmin``(例如参见 ``tests/core/admin.py):

class CustomBookAdmin(ImportMixin, ImportExportModelAdmin):
    resource_classes = [EBookResource]
    export_form_class = CustomExportForm

    def get_export_resource_kwargs(self, request, **kwargs):
        export_form = kwargs.get("export_form")
        if export_form:
            kwargs.update(author_id=export_form.cleaned_data["author"].id)
        return kwargs

admin.site.register(Book, CustomBookAdmin)

创建一个Resource子类来应用过滤器(例如参见``tests/core/admin.py``):

class EBookResource(ModelResource):
    def __init__(self, **kwargs):
        super().__init__()
        self.author_id = kwargs.get("author_id")

    def filter_export(self, queryset, **kwargs):
        return queryset.filter(author_id=self.author_id)

    class Meta:
        model = EBook

在此示例中,我们可以使用作者的姓名来过滤EBook导出。

  1. 创建一个自定义表单,将'author'定义为必填字段。

  2. 创建一个 'CustomBookAdmin' 类,该类定义了一个 Resource,并重写了 get_export_resource_kwargs()。这确保了作者 ID 会被传递给 Resource 构造函数。

  3. 创建一个:class:~import_export.resources.Resource,该资源使用``author_id``实例化,并可根据需要过滤查询集。

使用多种资源

可以设置多个资源来导入和导出`ModelAdmin`类。ImportMixinExportMixin``ImportExportMixin``和``ImportExportModelAdmin``类接受可下标类型(列表、元组等)作为``resource_classes``参数。

下标对象也可能从以下之一返回:

如果有多个资源,资源选择器会出现在导入/导出管理表单中。资源的显示名称可以通过`Meta`类的`name`参数更改。

使用多种资源:

from import_export import resources
from core.models import Book


class BookResource(resources.ModelResource):

    class Meta:
        model = Book


class BookNameResource(resources.ModelResource):

    class Meta:
        model = Book
        fields = ['id', 'name']
        name = "Export/Import only book names"


class CustomBookAdmin(ImportMixin, admin.ModelAdmin):
    resource_classes = [BookResource, BookNameResource]

如何动态设置resource值

在某些使用场景中,需要动态设置`Resource`中的值。例如,假设您通过Admin控制台进行导入,并希望在导入查询中使用与认证用户关联的值。

假设经过身份验证的用户(存储在``request``对象中)有一个名为``publisher_id``的属性。在导入过程中,我们希望仅过滤与该出版商相关联的书籍。

首先,重写 get_import_resource_kwargs() 方法以保留请求用户:

class BookAdmin(ImportExportMixin, admin.ModelAdmin):
    # attribute declarations not shown

    def get_import_resource_kwargs(self, request, *args, **kwargs):
        kwargs = super().get_resource_kwargs(request, *args, **kwargs)
        kwargs.update({"user": request.user})
        return kwargs

现在你可以向你的``Resource``添加一个构造函数来存储用户引用,然后重写``get_queryset()``以返回出版商的书籍:

class BookResource(ModelResource):

    def __init__(self, user):
        self.user = user

    def get_queryset(self):
        return self._meta.model.objects.filter(publisher_id=self.user.publisher_id)

    class Meta:
        model = Book

与第三方库的互操作性

import-export 扩展了 Django Admin 界面。有可能与其他同样使用 admin 界面的第三方库发生冲突。

django-admin-sortable2

由于与设置 change_list_template 存在冲突,引发了一些问题。此处 <https://github.com/jrief/django-admin-sortable2/issues/345#issuecomment-1680271337>`_ 提供了一个临时解决方案。另请参考 此问题。如需自行修补安装以解决此问题,可在此处 <https://github.com/django-import-export/django-import-export/pull/1607>`_ 获取补丁。

django-polymorphic

参考 此问题

模板因递归问题被跳过

参考 此问题

django-debug-toolbar

如果你使用导入导出功能与`django-debug-toolbar <https://pypi.org/project/django-debug-toolbar>`_一起,那么你需要配置``debug_toolbar=False``或``DEBUG=False``,据报道导入/导出时间会增加约10倍。

参考 此PR

安全

启用Admin界面意味着您应考虑安全影响。以下部分或全部要点可能相关。

是否存在不受信任导入的潜在风险?

  • 您的导入文件来源是什么?

  • 这是来自外部来源的数据可能不受信任吗?

  • 源数据是否可能包含恶意内容,例如脚本指令或Excel公式?

  • 即使数据来自可信来源,是否存在诸如HTML等内容在网页中渲染时可能导致问题?

导出的数据存在什么潜在风险?

  • 如果存储数据中存在恶意内容,导出此数据的风险是什么?

  • 不可信输入是否能在电子表格内执行?

  • 电子表格是否发送给其他可能无意中执行恶意内容的各方?

  • 数据能否导出为其他格式,例如CSV、TSV或ODS,然后使用Excel打开?

  • 导出的任何数据是否可以用HTML呈现?例如,csv被导出然后加载到另一个web应用程序中。在这种情况下,不受信任的输入可能包含恶意代码,例如活动脚本内容。

在所有情况下,部署实时Admin界面实例之前,你都应该查看`Django安全文档<https://docs.djangoproject.com/en/stable/topics/security/>`_。

降低安全风险

请仔细阅读以下主题以了解如何提高您的实施安全性。

清理导出

默认情况下,import-export不会对导入的数据进行清理或处理。恶意内容,如script指令,可能会被导入数据库,并且可以未经任何修改就被导出。

备注

如果将HTML内容导出为'html'格式,将被清理以移除可脚本化内容。此清理由``tablib``库执行。

你可以选择性地配置import-export以在导出时清理Excel公式数据。参见:ref:IMPORT_EXPORT_ESCAPE_FORMULAE_ON_EXPORT

启用此设置仅会清理通过管理界面导出的数据。如果以:ref:`编程方式<exporting_data>`导出数据,则需要自行应用清理操作。

限制格式

可以考虑限制可用的导入或导出格式类型。例如,如果您从不需要支持电子表格数据的导入或导出,可以从应用程序中移除该格式。

可以使用以下设置限制进出口:

  1. 导入导出格式

  2. 重要格式

  3. EXPORT_FORMATS

设置权限

考虑设置 permissions 以定义哪些用户可以导入和导出。

  1. IMPORT_EXPORT_IMPORT_PERMISSION_CODE

  2. IMPORT_EXPORT_EXPORT_PERMISSION_CODE

提出安全问题

参考 SECURITY.md 了解如何上报您在 import-export 中发现的安全问题。