openpyxl插入行后表格下方单元格值与样式丢失问题求助
openpyxl插入行后表格下方单元格值与样式丢失问题求助
我目前在用openpyxl处理xlsx模板文件:加载模板、填充数据后保存,核心需求是保留模板原有的格式(包括单元格样式、颜色、布局等)。其中有个关键操作是往表格里插入列表数据——我原本的计划是先给表格新增足够数量的行,再把数据逐个单元格写入新行,但现在遇到了一个棘手的问题:插入行之后,表格下方的所有单元格值、样式、颜色全部丢失了,只有合并单元格的尺寸、插入的图片还保留,而且表格下方仅剩我在脚本里手动设置的那些值。这个问题在模板的4个带表格的工作表里都会出现。
以下是我简化后的其中一个工作表的填充逻辑代码:
from openpyxl.worksheet.worksheet import Worksheet from typing import List, Tuple import copy # 假设相关的实体类(Product、ImportationProductLink等)已提前定义 def _fill_oferta_cliente( self, ws: Worksheet, table_data: List[Tuple[Product, ImportationProductLink]], offer_date, importation: Importation, containers: List[ContainersValue], ) -> None: TABLE_START_ROW = 6 TABLE_START_COL = 3 TABLE_NUM_COLS = 9 TABLE_DATA_LEN = len(table_data) INSERTED_ROWS = TABLE_DATA_LEN - 1 original_merges = self._capture_merges(ws) # 先解除所有合并单元格 for merge in list(ws.merged_cells): ws.unmerge_cells(str(merge)) # 为表格插入需要的行 ws.insert_rows(TABLE_START_ROW + 1, INSERTED_ROWS) # 填充表格数据 for idx, (product, link) in enumerate(table_data): target_row = TABLE_START_ROW + idx # 从第二行开始复制模板行的格式 if idx > 0: self._copy_row( ws=ws, source_row=TABLE_START_ROW, target_row=target_row, start_col=TABLE_START_COL, num_cols=TABLE_NUM_COLS, ) initial_form_row = 11 + idx values = [ product.tariff_heading, # Partida Arancelaria product.reference, # Referencia product.description, # Producto / Descripción product.unit_of_measure, # Unidad de medida link.units, # Unidades f"='F.PRECIO EXTENDIDA'!J{initial_form_row}", # PRECIO UNITARIO CIF USD f"='F.PRECIO EXTENDIDA'!S{initial_form_row}", # IMPORTE CIF USD f"='F.PRECIO CLIENTES'!J{initial_form_row-1}", # PRECIO UNITARIO CUP f"='F.PRECIO CLIENTES'!O{initial_form_row-1}", # IMPORTE CUP ] self._write_row( ws=ws, row=target_row, start_col=TABLE_START_COL, values=values, ) # 处理容器网格数据 SQUARE_SECTION_COLUMNS = 6 SQUARE_SECTION_START_ROW = 12 result = len(containers) // SQUARE_SECTION_COLUMNS exc = len(containers) % SQUARE_SECTION_COLUMNS container_square_rows = result + (1 if exc > 0 else 0) ws.insert_rows(SQUARE_SECTION_START_ROW - 1, container_square_rows - 1) INSERTED_ROWS = INSERTED_ROWS + container_square_rows - 1 for i in range(container_square_rows): j = 0 max_j = SQUARE_SECTION_COLUMNS * (i + 1) if (i + 1) == container_square_rows and exc > 0: max_j = exc else: break while j < max_j: container = containers[j] ws.cell(row=SQUARE_SECTION_START_ROW + i, column=j + 6).value = container.value j = j + 1 # 设置公式和静态值 ws[f"I{6+TABLE_DATA_LEN}"] = f"=SUM(I6:I{6+INSERTED_ROWS})" ws[f"I{8+TABLE_DATA_LEN}"] = f"=+I{6+TABLE_DATA_LEN}*H{8+TABLE_DATA_LEN}" ws[f"K{8+TABLE_DATA_LEN}"] = f"=+K{6+TABLE_DATA_LEN}*H{8+TABLE_DATA_LEN}" ws[f"K{20+INSERTED_ROWS}"] = f"=+F.PRECIO!I37*H{9+INSERTED_ROWS}" ws[f"K{21+INSERTED_ROWS}"] = f"=+F.PRECIO!I38*H{9+INSERTED_ROWS}" ws[f"K{28+INSERTED_ROWS}"] = f"=SUM(K{24+INSERTED_ROWS}:K{27+INSERTED_ROWS})" ws[f"F{33+INSERTED_ROWS}"] = importation.client.legal_address ws[f"F{34+INSERTED_ROWS}"] = importation.client.user.phone_number ws[f"F{35+INSERTED_ROWS}"] = importation.client.user.email ws[f"F{36+INSERTED_ROWS}"] = importation.client.establishment_denomination ws[f"F{37+INSERTED_ROWS}"] = importation.delivery ws[f"F{39+INSERTED_ROWS}"] = importation.client.bank_name ws[f"F{40+INSERTED_ROWS}"] = importation.client.branch_office ws[f"F{41+INSERTED_ROWS}"] = importation.client.holder ws[f"F{42+INSERTED_ROWS}"] = importation.client.account_number ws["K2"].value = offer_date # 调整并恢复合并单元格 adjusted_merges = self._adjust_container_merges( original_merges, container_square_rows, ) self._restore_merges( ws=ws, merges=adjusted_merges, insert_row=7, inserted_rows=INSERTED_ROWS, ) def _write_row(self, ws: Worksheet, row: int, start_col: int, values: List) -> None: """将值写入指定行的连续列""" for offset, value in enumerate(values): ws.cell(row=row, column=start_col + offset, value=value) def _copy_row( self, ws: Worksheet, source_row: int, target_row: int, start_col: int, num_cols: int, ) -> None: """复制指定行的样式和值到目标行""" for col_offset in range(num_cols): col = start_col + col_offset source_cell = ws.cell(row=source_row, column=col) target_cell = ws.cell(row=target_row, column=col) target_cell.value = source_cell.value if source_cell.has_style: target_cell.font = copy(source_cell.font) target_cell.border = copy(source_cell.border) target_cell.fill = copy(source_cell.fill) target_cell.number_format = source_cell.number_format target_cell.alignment = copy(source_cell.alignment) def _capture_merges(self, ws: Worksheet): """捕获工作表中所有合并单元格,返回格式为(min_row, max_row, min_col, max_col)的元组列表""" merges = [] for mr in ws.merged_cells.ranges: merges.append((mr.min_row, mr.max_row, mr.min_col, mr.max_col)) return merges def _restore_merges( self, ws: Worksheet, merges, insert_row: int, inserted_rows: int, ): """根据插入行的位置调整合并单元格范围并恢复合并""" for min_row, max_row, min_col, max_col in merges: # 合并单元格完全在插入点上方:不调整 if max_row < insert_row: new_min_row = min_row new_max_row = max_row # 合并单元格完全在插入点下方:整体下移 elif min_row >= insert_row: new_min_row = min_row + inserted_rows new_max_row = max_row + inserted_rows # 合并单元格覆盖插入点:扩展行范围 else: new_min_row = min_row new_max_row = max_row + inserted_rows ws.merge_cells( start_row=new_min_row, end_row=new_max_row, start_column=min_col, end_column=max_col, ) def _adjust_container_merges( self, merges, container_square_rows: int, ): """调整容器区域的合并单元格行范围""" adjusted = [] for min_row, max_row, min_col, max_col in merges: if min_row == 12 and min_col in (3, 5): adjusted.append( ( min_row, max_row + (container_square_rows - 1), min_col, max_col, ) ) else: adjusted.append((min_row, max_row, min_col, max_col)) return adjusted
我已经尝试了先解除所有合并单元格、插入行后再恢复合并的操作,但还是没能解决表格下方内容丢失的问题。想请教各位,有没有遇到过类似的情况?或者能帮我分析下可能是什么原因导致的吗?




