"""CMS 导入 / 导出通用工具(基于 openpyxl)。""" import io from django.http import HttpResponse from openpyxl import Workbook, load_workbook XLSX_CONTENT_TYPE = ( 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ) def xlsx_response(filename, headers, rows): """生成 .xlsx 下载响应。 Args: filename: 下载文件名(含 .xlsx) headers: 表头列表 rows: 二维数据(list[list]);空则只导出表头(可作模板) """ wb = Workbook() ws = wb.active ws.append(list(headers)) for row in rows: ws.append(list(row)) buf = io.BytesIO() wb.save(buf) buf.seek(0) resp = HttpResponse(buf.getvalue(), content_type=XLSX_CONTENT_TYPE) resp['Content-Disposition'] = f'attachment; filename="{filename}"' return resp def rows_from_xlsx(file): """解析上传的 .xlsx,首行为表头,返回 list[dict](表头→单元格字符串)。 空行跳过;单元格统一转为去空格字符串(None → '')。 """ wb = load_workbook(file, read_only=True, data_only=True) ws = wb.active it = ws.iter_rows(values_only=True) try: header_cells = next(it) except StopIteration: return [] headers = [str(h).strip() if h is not None else '' for h in header_cells] result = [] for raw in it: if raw is None or all(c is None or str(c).strip() == '' for c in raw): continue row = {} for i, h in enumerate(headers): if not h: continue val = raw[i] if i < len(raw) else None row[h] = '' if val is None else str(val).strip() result.append(row) return result