feat(excel): 添加Excel导入/导出 (#5124)

* refactor(drf_renderer): 添加 ExcelRenderer 支持导出excel文件格式; 优化CSVRenderer, 抽象 BaseRenderer

* perf(renderer): 支持导出资源详情

* refactor(drf_parser): 添加 ExcelParser 支持导入excel文件格式; 优化CSVParser, 抽象 BaseParser

* refactor(drf_parser): 添加 ExcelParser 支持导入excel文件格式; 优化CSVParser, 抽象 BaseParser 2

* perf(renderer): 捕获renderer处理异常

* perf: 添加excel依赖包

* perf(drf): 优化导入导出错误日志

* perf: 添加依赖包 pyexcel-io==0.6.4

* perf: 添加依赖包pyexcel-xlsx==0.6.0

* feat: 修改drf/renderer&parser变量命名

* feat: 修改drf/renderer的bug

* feat: 修改drf/renderer&parser变量命名

Co-authored-by: Bai <bugatti_it@163.com>
This commit is contained in:
fit2bot
2020-12-07 15:23:05 +08:00
committed by GitHub
parent 619b521ea1
commit 43b5e97b95
10 changed files with 339 additions and 186 deletions

View File

@@ -1,83 +1,30 @@
# ~*~ coding: utf-8 ~*~
#
import unicodecsv
import codecs
from datetime import datetime
import unicodecsv
from six import BytesIO
from rest_framework.renderers import BaseRenderer
from rest_framework.utils import encoders, json
from common.utils import get_logger
logger = get_logger(__file__)
from .base import BaseFileRenderer
class JMSCSVRender(BaseRenderer):
class CSVFileRenderer(BaseFileRenderer):
media_type = 'text/csv'
format = 'csv'
@staticmethod
def _get_show_fields(fields, template):
if template == 'import':
return [v for k, v in fields.items() if not v.read_only and k != "org_id" and k != 'id']
elif template == 'update':
return [v for k, v in fields.items() if not v.read_only and k != "org_id"]
else:
return [v for k, v in fields.items() if not v.write_only and k != "org_id"]
writer = None
buffer = None
@staticmethod
def _gen_table(data, fields):
data = data[:10000]
yield ['*{}'.format(f.label) if f.required else f.label for f in fields]
def initial_writer(self):
csv_buffer = BytesIO()
csv_buffer.write(codecs.BOM_UTF8)
csv_writer = unicodecsv.writer(csv_buffer, encoding='utf-8')
self.buffer = csv_buffer
self.writer = csv_writer
for item in data:
row = [item.get(f.field_name) for f in fields]
yield row
def set_response_disposition(self, serializer, context):
response = context.get('response')
if response and hasattr(serializer, 'Meta') and \
hasattr(serializer.Meta, "model"):
model_name = serializer.Meta.model.__name__.lower()
now = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
filename = "{}_{}.csv".format(model_name, now)
disposition = 'attachment; filename="{}"'.format(filename)
response['Content-Disposition'] = disposition
def render(self, data, media_type=None, renderer_context=None):
renderer_context = renderer_context or {}
request = renderer_context['request']
template = request.query_params.get('template', 'export')
view = renderer_context['view']
if isinstance(data, dict):
data = data.get("results", [])
if template == 'import':
data = [data[0]] if data else data
data = json.loads(json.dumps(data, cls=encoders.JSONEncoder))
try:
serializer = view.get_serializer()
self.set_response_disposition(serializer, renderer_context)
except Exception as e:
logger.debug(e, exc_info=True)
value = 'The resource not support export!'.encode('utf-8')
else:
fields = serializer.fields
show_fields = self._get_show_fields(fields, template)
table = self._gen_table(data, show_fields)
csv_buffer = BytesIO()
csv_buffer.write(codecs.BOM_UTF8)
csv_writer = unicodecsv.writer(csv_buffer, encoding='utf-8')
for row in table:
csv_writer.writerow(row)
value = csv_buffer.getvalue()
def write_row(self, row):
self.writer.writerow(row)
def get_rendered_value(self):
value = self.buffer.getvalue()
return value