Django项目需要数据导入及导出,使用django-import-export实现
1.安装
pip install django-import-export==2.7.1
2.setting.py文件
INSTALLED_APPS = [
...
'import_export',
]```
3.admin.py文件
```python
from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
# 修改model路径
from ...models import Sample
# 修改resource路径(resource.py文件为新建文件)
from ...resource import SampleResource
class CommonAdmin(ImportExportModelAdmin):
"""统一封装 添加id过滤"""
list_filter = ('id', )
def get_list_display(self, request):
"""展示字段"""
return [field.name for field in self.model._meta.fields]
@admin.register(Sample)
class SampleAdmin(CommonAdmin):
"""SampleAdmin"""
resource_class = SampleResource
4.model.py文件
class Dataset(models.Model):
"""
Dataset
"""
dataset_id = models.CharField(max_length=255, primary_key=True)
title = models.CharField(max_length=255)
class Sample(models.Model):
"""
Sample
"""
dataset = models.ForeignKey(Dataset, on_delete=models.CASCADE)
sample_id = models.CharField(max_length=255)
sample_name = models.CharField(max_length=255, default='', blank=True)
5.resource.py 文件
class SampleResource(CommonResource):
"""
SampleResources
"""
# 此处dataset_id为外键关联,因此需定义dataset
dataset = fields.Field(
column_name='dataset_id',
attribute='dataset',
widget=ForeignKeyWidget(Dataset, 'dataset_id'))
class Meta:
"""
Meta
"""
model = Sample
# skip_diff开启会导致django-admin上传时显示空白,但是能正常导入
skip_diff = True
# 数据一致则跳过
skip_unchanged = True
# 设置数据库事务,一个失败全部回滚
use_transactions = True
# 导入每行都创建实例,用于触发Model的save函数。便于signal信号函数调用
force_init_instance = True
# 设置批量导入
use_bulk = True
batch_size = 1000
# 导入数据后逻辑(根据业务需求调整)
def after_import(self, dataset, result, using_transactions, dry_run, **kwargs):
"""上传数据后,更新es数据库中的sample字段 每次仅上传一个数据集"""
super().after_import(dataset, result, using_transactions, dry_run, **kwargs)
if not dry_run:
dataset_id = dataset.dict[0]['dataset_id'] if dataset.dict else None
if dataset_id:
dataset = Dataset.objects.get(dataset_id=dataset_id)
sample_data = get_nested_sample(dataset)
document = DatasetDocument.get(id=dataset_id)
document.sample = sample_data
document.save()
临时文件目录更改
背景:django-import-export在上传文件时会在系统的临时文件目录下创建临时文件,上传完成后再删掉文件。可根据需要对临时文件目录进行重写。
1.setting.py文件
# 自定义临时文件函数
IMPORT_EXPORT_TMP_STORAGE_CLASS = '***.import_tools.CustomTempFolderStorage'
# 自定义临时文件目录
PROJECT_TMP_FOLDER=/***/tmp/
2.import_tools.py文件
"""自定义导入模块临时文件路径"""
import os
import tempfile
from django.conf import settings
from import_export.tmp_storages import BaseStorage
class CustomTempFolderStorage(BaseStorage):
"""重写TempFolderStorage"""
temp_folder = settings.PROJECT_TMP_FOLDER
def __init__(self, name=None):
"""初始化临时文件路径"""
super().__init__(name)
# 确保目录存在
if not os.path.exists(self.temp_folder):
os.makedirs(self.temp_folder)
def open(self, mode='r'):
"""创建临时文件"""
if self.name:
return open(self.get_full_path(), mode) # pylint:disable=W1514
# dir:设置路径
tmp_file = tempfile.NamedTemporaryFile(delete=False, dir=self.temp_folder) # pylint:disable=R1732
self.name = tmp_file.name
return tmp_file
def save(self, data, mode='w'):
"""保存临时文件"""
with self.open(mode=mode) as file:
file.write(data)
def read(self, mode='r'): # pylint:disable=W0237
"""读取临时文件路径"""
with self.open(mode=mode) as file:
return file.read()
def remove(self):
"""删除临时文件"""
os.remove(self.get_full_path())
def get_full_path(self):
"""获取临时文件存放路径"""
return os.path.join(self.temp_folder, os.path.basename(self.name))