众所周知,在使用Django
时,经常会用到导入导出功能。Django
官方为我们提供了actions
插槽实现了绑定Model
的一些操作。
基于此,我们可以利用该函数实现选中下载,选中上传更新等操作。但是本文不讨论该函数,感兴趣的朋友可以参考:Django Admin actions。
本文讨论第三方库 django-import-export
,该库基于 tablib
(点此查看)实现了
Excel
JSON
YAML
Pandas DataFrames
HTML
Jira
LaTeX
TSV
ODS
CSV
DBF
上述文件的导入与导出。但是,如果我们需要导入的文件类型不在上述列表中又该如何操作?
下面通过上传 zip
压缩文件(zipfile
+django-import-export
)举例进行说明,正文开始。
1、定义 Format
class ZIPFormat(TablibFormat):
# 上传中间页显示的格式名称
title = 'zip'
# tablib需要定义的module名称,后续会通过split的方式进行切分校验
TABLIB_MODULE = 'tablib.formats._zip'
# 文件上传时校验的 request header type
CONTENT_TYPE = 'application/x-zip-compressed'
def create_dataset(self, in_stream, **kwargs):
"""
创建数据集
@params:in_stream: request content
"""
from zipfile import ZipFile
# 由于 zipfile 的原因,无法直接读取请求流,所以需要BytesIO进行转换
f = ZipFile(io.BytesIO(in_stream), 'r')
return f
@classmethod
def import_set(cls, dset, in_stream):
"""
鉴定是否有导入权限的函数,同时对数据集进行一些设置,没有该函数页面将不会显示导入按钮
同理,导出需实现export_set函数
同样,我们也可以通过 can_import(self),can_export(self)函数直接返回True
"""
dset.wipe()
dset.dict = in_stream
2、定义 Storage
class ZipStorage(TempFolderStorage):
"""
这里继承了 import_export.tmp_storages中的TempFolderStorage类
亦可以继承同级的BaseStorage类,但是需要多实现一个remove方法
"""
def save(self, data):
"""
必须实现的save方法
"""
from zipfile import ZipFile
with ZipFile(self.get_full_path(), 'w') as file:
file.write(data)
def read(self):
"""
必须实现的read方法,这里返回了使用zipfile读取文件后的压缩包内部数据
"""
from zipfile import ZipFile
with ZipFile(self.get_full_path(), 'r') as file:
return file.infolist()
3、编写 Resource
class ZIPResource(resources.ModelResource):
"""
这里通过绑定指定类进行编写,实际使用时仅显示不校验
"""
class Meta:
model = BookModel
fields = ('avator', )
def import_data(self, dataset, dry_run=False, raise_errors=False, use_transactions=None, collect_failed_rows=False, rollback_on_validation_errors=False, **kwargs):
"""
一步到位直接重写import_data方法,自定义自己所需要的操作
@params:dataset: 前文中的dataset
其余参数这里不再赘述
"""
# 指定上传路径
ep = Path(BASE_DIR, "images", 'upload').resolve()
# 获取压缩包内的图片数量
nums = dataset.infolist()
# 解压
dataset.extractall(ep, nums)
# 自定义操作,model更新等...
custom_operation(nums)
# 自定义返回对象
result = self.get_result_class()()
# 设置返回对象的总添加数
# 可以根据需求对更新数量同步更改
result.totals = len(nums)
return result
4、注册自定义的 Format
from tablib.formats import registry
# 导入tablib的registry实例对自定义的Format进行注册
registry.register("zip", ZIPFormat())
5、(可选)自定义返回消息
from django.contrib import messages
from import_export.admin import ImportMixin
class CustomImportMixin(ImportMixin):
"""
自定义mixin工具类,继承ImportMixin,export同理
"""
def add_success_message(self, result, request):
"""
请求成功时页面弹出的消息
通过 django 的 messages 进行消息的通信
"""
success_message = '文件上传成功, 本次共解压 %s 张图片.' % result.totals
messages.success(request, success_message)
通过上述步骤,我们便可以在 Django Admin中实现自定文件类型的操作,同时避免了绑定Model的操作。
当然,上述的操作只是作为初级使用文档,更多的好玩用法可以参考官方文档与阅读源码。
最后
凡有所学,皆成性格。