管理员集成¶
导入导出功能的主要特点之一是支持与Django Admin站点的集成。这为导入和导出Django对象提供了一个便捷的界面。有关如何启用和配置admin站点的详细信息,请参阅`Django Admin文档<https://docs.djangoproject.com/en/stable/ref/contrib/admin/>`_。
你也可以安装并运行 示例应用 来熟悉 Admin 集成。
将进出口与您的应用程序集成需要额外配置。
管理员集成通过继承 ImportExportModelAdmin 或可用的混入类之一(ImportMixin、ExportMixin、ImportExportMixin)实现:
# 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中以选择下拉框的形式呈现。
带有导入和导出按钮的变更视图的屏幕截图。¶
导入¶
要启用导入功能,请子类化 ImportExportModelAdmin 或使用其中一个可用的 mixin,例如 ImportMixin 或 ImportExportMixin。
启用导入功能意味着UI按钮将自动显示在Admin页面上:
点击后,用户将被引导至导入工作流程。默认情况下,导入为两步流程,但也可配置为单步流程(参见 IMPORT_EXPORT_SKIP_ADMIN_CONFIRM)。
两步流程是:
选择要导入的文件和格式。
预览导入数据并确认导入。
“导入”视图的屏幕截图。¶
“确认导入”视图的截图。¶
导入确认¶
为支持导入确认,上传的数据在步骤1(选择文件)后被写入临时存储,并在步骤2(导入确认)后读取以进行最终导入。
有三种临时存储机制。
主机服务器上的临时文件存储(默认)。这仅适用于开发环境。不建议在生产站点使用临时文件系统存储。
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_UI 或 IMPORT_EXPORT_SKIP_ADMIN_EXPORT_UI 标志,或者通过设置 skip_export_form_from_action 或 skip_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>`导入电子书演示了这一点。
自定义导入视图的屏幕截图。¶
自定义表单(例如参见``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筛选导出数据。
自定义导出视图的屏幕截图。¶
自定义表单(例如参见``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导出。
创建一个自定义表单,将'author'定义为必填字段。
创建一个 'CustomBookAdmin' 类,该类定义了一个
Resource,并重写了get_export_resource_kwargs()。这确保了作者 ID 会被传递给Resource构造函数。创建一个:class:~import_export.resources.Resource,该资源使用``author_id``实例化,并可根据需要过滤查询集。
使用多种资源¶
可以设置多个资源来导入和导出`ModelAdmin`类。ImportMixin、ExportMixin、``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>`导出数据,则需要自行应用清理操作。
限制格式¶
可以考虑限制可用的导入或导出格式类型。例如,如果您从不需要支持电子表格数据的导入或导出,可以从应用程序中移除该格式。
可以使用以下设置限制进出口:
导入导出格式
重要格式
设置权限¶
考虑设置 permissions 以定义哪些用户可以导入和导出。
提出安全问题¶
参考 SECURITY.md 了解如何上报您在 import-export 中发现的安全问题。