Anonymous
Openpyxl удаляет все значения и стили ячеек, которые находятся ниже вставленных мной значений.
Сообщение
Anonymous » 25 янв 2026, 12:17
Я использую openpyxl для загрузки файла шаблона .xlsx, добавления в него данных и сохранения. Я делаю это, чтобы сохранить исходное форматирование файла (стиль, цвета, макет...). Часть информации, которую мне нужно вставить, представляет собой список, который находится внутри таблицы в начале файла. Для этого я решил добавить в таблицу необходимое количество строк, а затем вставить данные (ячейка за ячейкой) в созданные строки. Когда я это делаю, все под таблицей, включая значения, стили и цвета, исчезает. Объединенные ячейки остаются того же размера, как и изображения. В ячейках под таблицей остались только значения, которые я ввел в скрипт. В шаблоне 4 листа (это происходит на всех листах с таблицами), вот код одного из них упрощенный:
Код: Выделить всё
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,
)
# containers grid
SQUARE_SECTION_COLUMNS = 6
SQUARE_SECTION_START_ROW = 12
result = len(containers) // SQUARE_SECTION_COLUMNS
print("result")
print(result)
exc = len(containers) % SQUARE_SECTION_COLUMNS
print("exc")
print(exc)
container_square_rows = result + (1 if exc > 0 else 0)
print("container_square_rows")
print(container_square_rows)
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
# formulas and static values
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):
"""
Returns a list of tuples:
(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 the merge is completely above the insertion point → unchanged
if max_row < insert_row:
new_min_row = min_row
new_max_row = max_row
# If the merge is completely below → shift down
elif min_row >= insert_row:
new_min_row = min_row + inserted_rows
new_max_row = max_row + inserted_rows
# If it intersects the insertion point → extend
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:
# C12 = columna 3, E12 = columna 5
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
Есть идеи, что является причиной этого?
Подробнее здесь:
https://stackoverflow.com/questions/798 ... ted-values
1769332638
Anonymous
Я использую openpyxl для загрузки файла шаблона .xlsx, добавления в него данных и сохранения. Я делаю это, чтобы сохранить исходное форматирование файла (стиль, цвета, макет...). Часть информации, которую мне нужно вставить, представляет собой список, который находится внутри таблицы в начале файла. Для этого я решил добавить в таблицу необходимое количество строк, а затем вставить данные (ячейка за ячейкой) в созданные строки. Когда я это делаю, все под таблицей, включая значения, стили и цвета, исчезает. Объединенные ячейки остаются того же размера, как и изображения. В ячейках под таблицей остались только значения, которые я ввел в скрипт. В шаблоне 4 листа (это происходит на всех листах с таблицами), вот код одного из них упрощенный: [code]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, ) # containers grid SQUARE_SECTION_COLUMNS = 6 SQUARE_SECTION_START_ROW = 12 result = len(containers) // SQUARE_SECTION_COLUMNS print("result") print(result) exc = len(containers) % SQUARE_SECTION_COLUMNS print("exc") print(exc) container_square_rows = result + (1 if exc > 0 else 0) print("container_square_rows") print(container_square_rows) 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 # formulas and static values 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): """ Returns a list of tuples: (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 the merge is completely above the insertion point → unchanged if max_row < insert_row: new_min_row = min_row new_max_row = max_row # If the merge is completely below → shift down elif min_row >= insert_row: new_min_row = min_row + inserted_rows new_max_row = max_row + inserted_rows # If it intersects the insertion point → extend 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: # C12 = columna 3, E12 = columna 5 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 [/code] Есть идеи, что является причиной этого? Подробнее здесь: [url]https://stackoverflow.com/questions/79869671/openpyxl-deletes-all-cells-values-and-styles-that-are-below-my-inserted-values[/url]