[{"data":1,"prerenderedAt":2260},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formula-to-excel-cell-with-openpyxl":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formula-to-excel-cell-with-openpyxl":2252},{"id":4,"title":5,"body":6,"dateModified":2222,"datePublished":2222,"description":2223,"extension":2224,"faq":2225,"meta":2234,"navigation":212,"path":2245,"seo":2246,"slug":2248,"stem":2249,"type":2250,"__hash__":2251},"docs\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formula-to-excel-cell-with-openpyxl\u002Findex.md","Write a Formula to an Excel Cell with openpyxl",{"type":7,"value":8,"toc":2209},"minimark",[9,28,142,147,150,174,177,181,184,423,437,440,487,490,494,506,625,699,713,717,724,1148,1160,1164,1167,1377,1462,1470,1474,1477,1820,1823,1826,1948,1951,1955,2084,2088,2095,2099,2111,2115,2122,2132,2146,2155,2159,2162,2171,2174,2205],[10,11,12,13,17,18,21,22,27],"p",{},"Writing a formula with ",[14,15,16],"code",{},"openpyxl"," is a single assignment: give the cell a string that starts with ",[14,19,20],{},"=",". The complications are never in the assignment — they are in what you assign, whether the destination cell's format lets the formula run, and how you confirm the file is right without opening Excel. This guide, part of ",[23,24,26],"a",{"href":25},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002F","Working with Excel Formulas in Python",", covers all three.",[29,30,38,39,38,43,38,47,38,54,38,64,38,71,38,77,38,82,38,91,38,97,38,102,38,107,38,112,38,117,38,120,38,125,38,130,38,134,38,138],"svg",{"viewBox":31,"role":32,"ariaLabelledBy":33,"xmlns":36,"style":37},"0 0 720 210","img",[34,35],"wf-one-t","wf-one-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:720px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[40,41,42],"title",{"id":34},"One assignment, two different things stored in the cell",[44,45,46],"desc",{"id":35},"Assigning the string equals B2 times C2 stores formula text in the cell. The value slot next to it stays empty until Excel opens the file, evaluates the formula and caches the number 79.96.",[48,49],"rect",{"x":50,"y":50,"width":51,"height":52,"fill":53},"0","720","210","#ffffff",[48,55],{"x":56,"y":57,"width":58,"height":59,"rx":60,"fill":61,"stroke":62,"style":63},"24","30","252","52","10","#ebebfd","var(--brand,#5b5cf0)","stroke-width:2px",[65,66,70],"text",{"x":67,"y":68,"style":69},"150","62","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,monospace","ws[\"D2\"] = \"=B2*C2\"",[72,73],"line",{"x1":74,"y1":75,"x2":76,"y2":75,"stroke":62,"style":63},"280","56","332",[78,79],"polygon",{"points":80,"fill":81},"336,56 324,50 324,62","#5b5cf0",[48,83],{"x":84,"y":85,"width":86,"height":87,"rx":88,"fill":89,"stroke":90},"338","20","358","72","12","#f0f4ff","var(--line,#cdd5e6)",[65,92,96],{"x":93,"y":94,"style":95},"517","44","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","cell D2 in the saved file",[65,98,101],{"x":93,"y":99,"style":100},"66","font-size:12px;fill:var(--muted,#5b6780);text-anchor:middle","formula: \"=B2*C2\"",[65,103,106],{"x":93,"y":104,"style":105},"84","font-size:12px;fill:var(--danger,#dc2626);text-anchor:middle","value: empty",[48,108],{"x":84,"y":109,"width":86,"height":87,"rx":88,"fill":110,"stroke":111,"style":63},"118","#d9f4f1","var(--teal,#0f9488)",[65,113,116],{"x":93,"y":114,"style":115},"142","font-size:12.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","after Excel opens and saves",[65,118,101],{"x":93,"y":119,"style":100},"164",[65,121,124],{"x":93,"y":122,"style":123},"182","font-size:12px;fill:var(--text,#172033);text-anchor:middle","value: 79.96",[72,126],{"x1":93,"y1":127,"x2":93,"y2":128,"stroke":111,"style":129},"94","114","stroke-width:1.5px",[78,131],{"points":132,"fill":133},"517,118 511,106 523,106","#0f766e",[65,135,137],{"x":67,"y":136,"style":100},"132","Python stores text.",[65,139,141],{"x":67,"y":140,"style":100},"154","Excel supplies the number.",[143,144,146],"h2",{"id":145},"prerequisites","Prerequisites",[10,148,149],{},"Install openpyxl into your environment:",[151,152,157],"pre",{"className":153,"code":154,"language":155,"meta":156,"style":156},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[14,158,159],{"__ignoreMap":156},[160,161,163,167,171],"span",{"class":72,"line":162},1,[160,164,166],{"class":165},"sMTad","pip",[160,168,170],{"class":169},"srMev"," install",[160,172,173],{"class":169}," openpyxl\n",[10,175,176],{},"Nothing else is required — no Excel installation, no COM bridge. The examples below run identically on Windows, macOS and Linux.",[143,178,180],{"id":179},"step-1-write-the-formula","Step 1: write the formula",[10,182,183],{},"Create a small workbook, add data, then assign a formula to the cell that should carry it:",[151,185,189],{"className":186,"code":187,"language":188,"meta":156,"style":156},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\n\nwb = Workbook()\nws = wb.active\nws.title = \"Orders\"\n\nws.append([\"SKU\", \"Quantity\", \"Unit_Price\", \"Line_Total\"])\nws.append([\"A-100\", 4, 19.99])\nws.append([\"B-200\", 2, 49.50])\n\nws[\"D2\"] = \"=B2*C2\"\nws[\"D3\"] = \"=B3*C3\"\nws[\"C5\"] = \"Total\"\nws[\"D5\"] = \"=SUM(D2:D3)\"\n\nwb.save(\"orders.xlsx\")\nprint(ws[\"D5\"].value)     # '=SUM(D2:D3)'\n","python",[14,190,191,207,214,225,236,247,252,280,301,321,326,343,358,373,388,393,405],{"__ignoreMap":156},[160,192,193,197,201,204],{"class":72,"line":162},[160,194,196],{"class":195},"s-kum","from",[160,198,200],{"class":199},"skGVy"," openpyxl ",[160,202,203],{"class":195},"import",[160,205,206],{"class":199}," Workbook\n",[160,208,210],{"class":72,"line":209},2,[160,211,213],{"emptyLinePlaceholder":212},true,"\n",[160,215,217,220,222],{"class":72,"line":216},3,[160,218,219],{"class":199},"wb ",[160,221,20],{"class":195},[160,223,224],{"class":199}," Workbook()\n",[160,226,228,231,233],{"class":72,"line":227},4,[160,229,230],{"class":199},"ws ",[160,232,20],{"class":195},[160,234,235],{"class":199}," wb.active\n",[160,237,239,242,244],{"class":72,"line":238},5,[160,240,241],{"class":199},"ws.title ",[160,243,20],{"class":195},[160,245,246],{"class":169}," \"Orders\"\n",[160,248,250],{"class":72,"line":249},6,[160,251,213],{"emptyLinePlaceholder":212},[160,253,255,258,261,264,267,269,272,274,277],{"class":72,"line":254},7,[160,256,257],{"class":199},"ws.append([",[160,259,260],{"class":169},"\"SKU\"",[160,262,263],{"class":199},", ",[160,265,266],{"class":169},"\"Quantity\"",[160,268,263],{"class":199},[160,270,271],{"class":169},"\"Unit_Price\"",[160,273,263],{"class":199},[160,275,276],{"class":169},"\"Line_Total\"",[160,278,279],{"class":199},"])\n",[160,281,283,285,288,290,294,296,299],{"class":72,"line":282},8,[160,284,257],{"class":199},[160,286,287],{"class":169},"\"A-100\"",[160,289,263],{"class":199},[160,291,293],{"class":292},"sP0c6","4",[160,295,263],{"class":199},[160,297,298],{"class":292},"19.99",[160,300,279],{"class":199},[160,302,304,306,309,311,314,316,319],{"class":72,"line":303},9,[160,305,257],{"class":199},[160,307,308],{"class":169},"\"B-200\"",[160,310,263],{"class":199},[160,312,313],{"class":292},"2",[160,315,263],{"class":199},[160,317,318],{"class":292},"49.50",[160,320,279],{"class":199},[160,322,324],{"class":72,"line":323},10,[160,325,213],{"emptyLinePlaceholder":212},[160,327,329,332,335,338,340],{"class":72,"line":328},11,[160,330,331],{"class":199},"ws[",[160,333,334],{"class":169},"\"D2\"",[160,336,337],{"class":199},"] ",[160,339,20],{"class":195},[160,341,342],{"class":169}," \"=B2*C2\"\n",[160,344,346,348,351,353,355],{"class":72,"line":345},12,[160,347,331],{"class":199},[160,349,350],{"class":169},"\"D3\"",[160,352,337],{"class":199},[160,354,20],{"class":195},[160,356,357],{"class":169}," \"=B3*C3\"\n",[160,359,361,363,366,368,370],{"class":72,"line":360},13,[160,362,331],{"class":199},[160,364,365],{"class":169},"\"C5\"",[160,367,337],{"class":199},[160,369,20],{"class":195},[160,371,372],{"class":169}," \"Total\"\n",[160,374,376,378,381,383,385],{"class":72,"line":375},14,[160,377,331],{"class":199},[160,379,380],{"class":169},"\"D5\"",[160,382,337],{"class":199},[160,384,20],{"class":195},[160,386,387],{"class":169}," \"=SUM(D2:D3)\"\n",[160,389,391],{"class":72,"line":390},15,[160,392,213],{"emptyLinePlaceholder":212},[160,394,396,399,402],{"class":72,"line":395},16,[160,397,398],{"class":199},"wb.save(",[160,400,401],{"class":169},"\"orders.xlsx\"",[160,403,404],{"class":199},")\n",[160,406,408,411,414,416,419],{"class":72,"line":407},17,[160,409,410],{"class":292},"print",[160,412,413],{"class":199},"(ws[",[160,415,380],{"class":169},[160,417,418],{"class":199},"].value)     ",[160,420,422],{"class":421},"s-wDw","# '=SUM(D2:D3)'\n",[10,424,425,426,428,429,432,433,436],{},"The ",[14,427,410],{}," returns the formula text, because openpyxl has no calculation engine — it writes what you give it and reads back the same string. Open the saved file in Excel or LibreOffice and ",[14,430,431],{},"D5"," shows ",[14,434,435],{},"178.84",".",[10,438,439],{},"Any Excel function works the same way, including ones openpyxl knows nothing about:",[151,441,443],{"className":186,"code":442,"language":188,"meta":156,"style":156},"ws[\"E2\"] = '=IF(D2>100,\"large\",\"small\")'\nws[\"E3\"] = '=TEXT(TODAY(),\"yyyy-mm-dd\")'\nws[\"E4\"] = '=IFERROR(VLOOKUP(A2,Prices!$A$2:$B$50,2,FALSE),0)'\n",[14,444,445,459,473],{"__ignoreMap":156},[160,446,447,449,452,454,456],{"class":72,"line":162},[160,448,331],{"class":199},[160,450,451],{"class":169},"\"E2\"",[160,453,337],{"class":199},[160,455,20],{"class":195},[160,457,458],{"class":169}," '=IF(D2>100,\"large\",\"small\")'\n",[160,460,461,463,466,468,470],{"class":72,"line":209},[160,462,331],{"class":199},[160,464,465],{"class":169},"\"E3\"",[160,467,337],{"class":199},[160,469,20],{"class":195},[160,471,472],{"class":169}," '=TEXT(TODAY(),\"yyyy-mm-dd\")'\n",[160,474,475,477,480,482,484],{"class":72,"line":216},[160,476,331],{"class":199},[160,478,479],{"class":169},"\"E4\"",[160,481,337],{"class":199},[160,483,20],{"class":195},[160,485,486],{"class":169}," '=IFERROR(VLOOKUP(A2,Prices!$A$2:$B$50,2,FALSE),0)'\n",[10,488,489],{},"Note the outer single quotes in Python whenever the formula itself contains double quotes. That is a Python quoting matter, not an Excel one; the string that reaches the file is identical either way.",[143,491,493],{"id":492},"step-2-avoid-the-text-format-trap","Step 2: avoid the Text-format trap",[10,495,496,497,500,501,505],{},"If a cell's number format is ",[14,498,499],{},"Text",", Excel shows the formula rather than running it — a common surprise when writing into a ",[23,502,504],{"href":503},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002F","template"," where someone formatted a column as text years ago. Reset the format before you assign:",[151,507,509],{"className":186,"code":508,"language":188,"meta":156,"style":156},"from openpyxl import load_workbook\n\nwb = load_workbook(\"template.xlsx\")\nws = wb[\"Orders\"]\n\ncell = ws[\"D2\"]\nif cell.number_format == \"@\":          # \"@\" is Excel's Text format code\n    cell.number_format = \"General\"\n\ncell.value = \"=B2*C2\"\nwb.save(\"filled.xlsx\")\n",[14,510,511,522,526,540,555,559,573,593,603,607,616],{"__ignoreMap":156},[160,512,513,515,517,519],{"class":72,"line":162},[160,514,196],{"class":195},[160,516,200],{"class":199},[160,518,203],{"class":195},[160,520,521],{"class":199}," load_workbook\n",[160,523,524],{"class":72,"line":209},[160,525,213],{"emptyLinePlaceholder":212},[160,527,528,530,532,535,538],{"class":72,"line":216},[160,529,219],{"class":199},[160,531,20],{"class":195},[160,533,534],{"class":199}," load_workbook(",[160,536,537],{"class":169},"\"template.xlsx\"",[160,539,404],{"class":199},[160,541,542,544,546,549,552],{"class":72,"line":227},[160,543,230],{"class":199},[160,545,20],{"class":195},[160,547,548],{"class":199}," wb[",[160,550,551],{"class":169},"\"Orders\"",[160,553,554],{"class":199},"]\n",[160,556,557],{"class":72,"line":238},[160,558,213],{"emptyLinePlaceholder":212},[160,560,561,564,566,569,571],{"class":72,"line":249},[160,562,563],{"class":199},"cell ",[160,565,20],{"class":195},[160,567,568],{"class":199}," ws[",[160,570,334],{"class":169},[160,572,554],{"class":199},[160,574,575,578,581,584,587,590],{"class":72,"line":254},[160,576,577],{"class":195},"if",[160,579,580],{"class":199}," cell.number_format ",[160,582,583],{"class":195},"==",[160,585,586],{"class":169}," \"@\"",[160,588,589],{"class":199},":          ",[160,591,592],{"class":421},"# \"@\" is Excel's Text format code\n",[160,594,595,598,600],{"class":72,"line":282},[160,596,597],{"class":199},"    cell.number_format ",[160,599,20],{"class":195},[160,601,602],{"class":169}," \"General\"\n",[160,604,605],{"class":72,"line":303},[160,606,213],{"emptyLinePlaceholder":212},[160,608,609,612,614],{"class":72,"line":323},[160,610,611],{"class":199},"cell.value ",[160,613,20],{"class":195},[160,615,342],{"class":169},[160,617,618,620,623],{"class":72,"line":328},[160,619,398],{"class":199},[160,621,622],{"class":169},"\"filled.xlsx\"",[160,624,404],{"class":199},[29,626,38,631,38,634,38,637,38,640,38,647,38,652,38,658,38,663,38,667,38,670,38,674,38,677,38,682,38,685,38,689,38,692,38,695],{"viewBox":627,"role":32,"ariaLabelledBy":628,"xmlns":36,"style":37},"0 0 720 236",[629,630],"wf-text-t","wf-text-d",[40,632,633],{"id":629},"The same formula in a Text-formatted cell and a General cell",[44,635,636],{"id":630},"Side by side comparison. With number format @ the cell displays the literal text equals B2 times C2 and no calculation happens. With number format General the same string is treated as a formula and Excel displays 79.96.",[48,638],{"x":50,"y":50,"width":51,"height":639,"fill":53},"236",[48,641],{"x":56,"y":56,"width":642,"height":643,"rx":644,"fill":645,"stroke":646},"320","184","14","#fce9e9","var(--danger,#dc2626)",[65,648,651],{"x":643,"y":649,"style":650},"50","font-size:13px;font-weight:700;fill:var(--danger,#dc2626);text-anchor:middle","number_format = \"@\"",[48,653],{"x":654,"y":99,"width":655,"height":656,"rx":657,"fill":53,"stroke":90},"48","272","40","8",[65,659,662],{"x":643,"y":660,"style":661},"91","font-size:12.5px;fill:var(--text,#172033);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,monospace","=B2*C2",[65,664,666],{"x":643,"y":665,"style":100},"128","Excel displays the characters",[65,668,669],{"x":643,"y":67,"style":100},"no calculation, no result",[65,671,673],{"x":643,"y":122,"style":672},"font-size:12px;font-weight:700;fill:var(--danger,#dc2626);text-anchor:middle","looks like a bug in your script",[48,675],{"x":676,"y":56,"width":642,"height":643,"rx":644,"fill":110,"stroke":111},"376",[65,678,681],{"x":679,"y":649,"style":680},"536","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","number_format = \"General\"",[48,683],{"x":684,"y":99,"width":655,"height":656,"rx":657,"fill":53,"stroke":90},"400",[65,686,688],{"x":679,"y":660,"style":687},"font-size:14px;font-weight:700;fill:var(--text,#172033);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,monospace","79.96",[65,690,691],{"x":679,"y":665,"style":100},"the string is parsed as a formula",[65,693,694],{"x":679,"y":67,"style":100},"the result is cached on save",[65,696,698],{"x":679,"y":122,"style":697},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","what you wanted",[10,700,701,702,705,706,709,710,712],{},"The same trap has a second face: a cell that already holds a formula keeps its number format when you overwrite it, so a currency-formatted cell stays currency-formatted. That is usually welcome, but it means a percentage cell that inherits ",[14,703,704],{},"0.0%"," will display ",[14,707,708],{},"7996.0%"," for a value of ",[14,711,688],{}," until you set the format you actually want.",[143,714,716],{"id":715},"step-3-build-references-from-indexes","Step 3: build references from indexes",[10,718,719,720,723],{},"Hardcoding ",[14,721,722],{},"D2"," works for one cell. For a generated report, translate column numbers into letters so the formula follows the layout:",[151,725,727],{"className":186,"code":726,"language":188,"meta":156,"style":156},"from openpyxl import Workbook\nfrom openpyxl.utils import get_column_letter\n\nheaders = [\"SKU\", \"Quantity\", \"Unit_Price\", \"Line_Total\"]\ndata = [(\"A-100\", 4, 19.99), (\"B-200\", 2, 49.50), (\"C-300\", 7, 12.25)]\n\nwb = Workbook()\nws = wb.active\nws.append(headers)\n\nqty = get_column_letter(headers.index(\"Quantity\") + 1)          # 'B'\nprice = get_column_letter(headers.index(\"Unit_Price\") + 1)      # 'C'\ntotal = get_column_letter(headers.index(\"Line_Total\") + 1)      # 'D'\n\nfor row in data:\n    ws.append(row)\n    r = ws.max_row\n    ws[f\"{total}{r}\"] = f\"={qty}{r}*{price}{r}\"\n\nws[f\"{price}{ws.max_row + 2}\"] = \"Total\"\nws[f\"{total}{ws.max_row}\"] = f\"=SUM({total}2:{total}{len(data) + 1})\"\n\nwb.save(\"orders_indexed.xlsx\")\n",[14,728,729,739,751,755,781,832,836,844,852,857,861,888,911,933,937,951,956,966,1034,1039,1072,1133,1138],{"__ignoreMap":156},[160,730,731,733,735,737],{"class":72,"line":162},[160,732,196],{"class":195},[160,734,200],{"class":199},[160,736,203],{"class":195},[160,738,206],{"class":199},[160,740,741,743,746,748],{"class":72,"line":209},[160,742,196],{"class":195},[160,744,745],{"class":199}," openpyxl.utils ",[160,747,203],{"class":195},[160,749,750],{"class":199}," get_column_letter\n",[160,752,753],{"class":72,"line":216},[160,754,213],{"emptyLinePlaceholder":212},[160,756,757,760,762,765,767,769,771,773,775,777,779],{"class":72,"line":227},[160,758,759],{"class":199},"headers ",[160,761,20],{"class":195},[160,763,764],{"class":199}," [",[160,766,260],{"class":169},[160,768,263],{"class":199},[160,770,266],{"class":169},[160,772,263],{"class":199},[160,774,271],{"class":169},[160,776,263],{"class":199},[160,778,276],{"class":169},[160,780,554],{"class":199},[160,782,783,786,788,791,793,795,797,799,801,804,806,808,810,812,814,816,819,821,824,826,829],{"class":72,"line":238},[160,784,785],{"class":199},"data ",[160,787,20],{"class":195},[160,789,790],{"class":199}," [(",[160,792,287],{"class":169},[160,794,263],{"class":199},[160,796,293],{"class":292},[160,798,263],{"class":199},[160,800,298],{"class":292},[160,802,803],{"class":199},"), (",[160,805,308],{"class":169},[160,807,263],{"class":199},[160,809,313],{"class":292},[160,811,263],{"class":199},[160,813,318],{"class":292},[160,815,803],{"class":199},[160,817,818],{"class":169},"\"C-300\"",[160,820,263],{"class":199},[160,822,823],{"class":292},"7",[160,825,263],{"class":199},[160,827,828],{"class":292},"12.25",[160,830,831],{"class":199},")]\n",[160,833,834],{"class":72,"line":249},[160,835,213],{"emptyLinePlaceholder":212},[160,837,838,840,842],{"class":72,"line":254},[160,839,219],{"class":199},[160,841,20],{"class":195},[160,843,224],{"class":199},[160,845,846,848,850],{"class":72,"line":282},[160,847,230],{"class":199},[160,849,20],{"class":195},[160,851,235],{"class":199},[160,853,854],{"class":72,"line":303},[160,855,856],{"class":199},"ws.append(headers)\n",[160,858,859],{"class":72,"line":323},[160,860,213],{"emptyLinePlaceholder":212},[160,862,863,866,868,871,873,876,879,882,885],{"class":72,"line":328},[160,864,865],{"class":199},"qty ",[160,867,20],{"class":195},[160,869,870],{"class":199}," get_column_letter(headers.index(",[160,872,266],{"class":169},[160,874,875],{"class":199},") ",[160,877,878],{"class":195},"+",[160,880,881],{"class":292}," 1",[160,883,884],{"class":199},")          ",[160,886,887],{"class":421},"# 'B'\n",[160,889,890,893,895,897,899,901,903,905,908],{"class":72,"line":345},[160,891,892],{"class":199},"price ",[160,894,20],{"class":195},[160,896,870],{"class":199},[160,898,271],{"class":169},[160,900,875],{"class":199},[160,902,878],{"class":195},[160,904,881],{"class":292},[160,906,907],{"class":199},")      ",[160,909,910],{"class":421},"# 'C'\n",[160,912,913,916,918,920,922,924,926,928,930],{"class":72,"line":360},[160,914,915],{"class":199},"total ",[160,917,20],{"class":195},[160,919,870],{"class":199},[160,921,276],{"class":169},[160,923,875],{"class":199},[160,925,878],{"class":195},[160,927,881],{"class":292},[160,929,907],{"class":199},[160,931,932],{"class":421},"# 'D'\n",[160,934,935],{"class":72,"line":375},[160,936,213],{"emptyLinePlaceholder":212},[160,938,939,942,945,948],{"class":72,"line":390},[160,940,941],{"class":195},"for",[160,943,944],{"class":199}," row ",[160,946,947],{"class":195},"in",[160,949,950],{"class":199}," data:\n",[160,952,953],{"class":72,"line":395},[160,954,955],{"class":199},"    ws.append(row)\n",[160,957,958,961,963],{"class":72,"line":407},[160,959,960],{"class":199},"    r ",[160,962,20],{"class":195},[160,964,965],{"class":199}," ws.max_row\n",[160,967,969,972,975,978,982,985,988,991,994,996,998,1000,1003,1006,1008,1011,1013,1015,1017,1020,1022,1025,1027,1029,1031],{"class":72,"line":968},18,[160,970,971],{"class":199},"    ws[",[160,973,974],{"class":195},"f",[160,976,977],{"class":169},"\"",[160,979,981],{"class":980},"sSjpA","{",[160,983,984],{"class":199},"total",[160,986,987],{"class":980},"}{",[160,989,990],{"class":199},"r",[160,992,993],{"class":980},"}",[160,995,977],{"class":169},[160,997,337],{"class":199},[160,999,20],{"class":195},[160,1001,1002],{"class":195}," f",[160,1004,1005],{"class":169},"\"=",[160,1007,981],{"class":980},[160,1009,1010],{"class":199},"qty",[160,1012,987],{"class":980},[160,1014,990],{"class":199},[160,1016,993],{"class":980},[160,1018,1019],{"class":169},"*",[160,1021,981],{"class":980},[160,1023,1024],{"class":199},"price",[160,1026,987],{"class":980},[160,1028,990],{"class":199},[160,1030,993],{"class":980},[160,1032,1033],{"class":169},"\"\n",[160,1035,1037],{"class":72,"line":1036},19,[160,1038,213],{"emptyLinePlaceholder":212},[160,1040,1042,1044,1046,1048,1050,1052,1054,1057,1059,1062,1064,1066,1068,1070],{"class":72,"line":1041},20,[160,1043,331],{"class":199},[160,1045,974],{"class":195},[160,1047,977],{"class":169},[160,1049,981],{"class":980},[160,1051,1024],{"class":199},[160,1053,987],{"class":980},[160,1055,1056],{"class":199},"ws.max_row ",[160,1058,878],{"class":195},[160,1060,1061],{"class":292}," 2",[160,1063,993],{"class":980},[160,1065,977],{"class":169},[160,1067,337],{"class":199},[160,1069,20],{"class":195},[160,1071,372],{"class":169},[160,1073,1075,1077,1079,1081,1083,1085,1087,1090,1092,1094,1096,1098,1100,1103,1105,1107,1109,1112,1114,1116,1118,1121,1124,1126,1128,1130],{"class":72,"line":1074},21,[160,1076,331],{"class":199},[160,1078,974],{"class":195},[160,1080,977],{"class":169},[160,1082,981],{"class":980},[160,1084,984],{"class":199},[160,1086,987],{"class":980},[160,1088,1089],{"class":199},"ws.max_row",[160,1091,993],{"class":980},[160,1093,977],{"class":169},[160,1095,337],{"class":199},[160,1097,20],{"class":195},[160,1099,1002],{"class":195},[160,1101,1102],{"class":169},"\"=SUM(",[160,1104,981],{"class":980},[160,1106,984],{"class":199},[160,1108,993],{"class":980},[160,1110,1111],{"class":169},"2:",[160,1113,981],{"class":980},[160,1115,984],{"class":199},[160,1117,987],{"class":980},[160,1119,1120],{"class":292},"len",[160,1122,1123],{"class":199},"(data) ",[160,1125,878],{"class":195},[160,1127,881],{"class":292},[160,1129,993],{"class":980},[160,1131,1132],{"class":169},")\"\n",[160,1134,1136],{"class":72,"line":1135},22,[160,1137,213],{"emptyLinePlaceholder":212},[160,1139,1141,1143,1146],{"class":72,"line":1140},23,[160,1142,398],{"class":199},[160,1144,1145],{"class":169},"\"orders_indexed.xlsx\"",[160,1147,404],{"class":199},[10,1149,1150,1151,1154,1155,1159],{},"Deriving the letters from ",[14,1152,1153],{},"headers.index(...)"," means a column added at position two does not silently point the multiplication at the wrong data. The same idea applies to the ",[23,1156,1158],{"href":1157},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002F","cell reference of a chart series"," and to any range you hand to conditional formatting.",[143,1161,1163],{"id":1162},"step-4-verify-what-landed-in-the-file","Step 4: verify what landed in the file",[10,1165,1166],{},"Reading the file back proves the formula was written and lets you assert on it in a test:",[151,1168,1170],{"className":186,"code":1169,"language":188,"meta":156,"style":156},"from openpyxl import load_workbook\n\nwb = load_workbook(\"orders_indexed.xlsx\")\nws = wb.active\n\nfor row in ws.iter_rows(min_row=2, max_row=4, min_col=4, max_col=4):\n    for cell in row:\n        assert isinstance(cell.value, str) and cell.value.startswith(\"=\"), (\n            f\"{cell.coordinate} is not a formula: {cell.value!r}\"\n        )\n\nprint(\"formulas present:\", [c.value for c in ws[\"D\"][1:4]])\n",[14,1171,1172,1182,1186,1198,1206,1210,1259,1272,1300,1329,1334,1338],{"__ignoreMap":156},[160,1173,1174,1176,1178,1180],{"class":72,"line":162},[160,1175,196],{"class":195},[160,1177,200],{"class":199},[160,1179,203],{"class":195},[160,1181,521],{"class":199},[160,1183,1184],{"class":72,"line":209},[160,1185,213],{"emptyLinePlaceholder":212},[160,1187,1188,1190,1192,1194,1196],{"class":72,"line":216},[160,1189,219],{"class":199},[160,1191,20],{"class":195},[160,1193,534],{"class":199},[160,1195,1145],{"class":169},[160,1197,404],{"class":199},[160,1199,1200,1202,1204],{"class":72,"line":227},[160,1201,230],{"class":199},[160,1203,20],{"class":195},[160,1205,235],{"class":199},[160,1207,1208],{"class":72,"line":238},[160,1209,213],{"emptyLinePlaceholder":212},[160,1211,1212,1214,1216,1218,1221,1225,1227,1229,1231,1234,1236,1238,1240,1243,1245,1247,1249,1252,1254,1256],{"class":72,"line":249},[160,1213,941],{"class":195},[160,1215,944],{"class":199},[160,1217,947],{"class":195},[160,1219,1220],{"class":199}," ws.iter_rows(",[160,1222,1224],{"class":1223},"sa561","min_row",[160,1226,20],{"class":195},[160,1228,313],{"class":292},[160,1230,263],{"class":199},[160,1232,1233],{"class":1223},"max_row",[160,1235,20],{"class":195},[160,1237,293],{"class":292},[160,1239,263],{"class":199},[160,1241,1242],{"class":1223},"min_col",[160,1244,20],{"class":195},[160,1246,293],{"class":292},[160,1248,263],{"class":199},[160,1250,1251],{"class":1223},"max_col",[160,1253,20],{"class":195},[160,1255,293],{"class":292},[160,1257,1258],{"class":199},"):\n",[160,1260,1261,1264,1267,1269],{"class":72,"line":254},[160,1262,1263],{"class":195},"    for",[160,1265,1266],{"class":199}," cell ",[160,1268,947],{"class":195},[160,1270,1271],{"class":199}," row:\n",[160,1273,1274,1277,1280,1283,1286,1288,1291,1294,1297],{"class":72,"line":282},[160,1275,1276],{"class":195},"        assert",[160,1278,1279],{"class":292}," isinstance",[160,1281,1282],{"class":199},"(cell.value, ",[160,1284,1285],{"class":292},"str",[160,1287,875],{"class":199},[160,1289,1290],{"class":195},"and",[160,1292,1293],{"class":199}," cell.value.startswith(",[160,1295,1296],{"class":169},"\"=\"",[160,1298,1299],{"class":199},"), (\n",[160,1301,1302,1305,1307,1309,1312,1314,1317,1319,1322,1325,1327],{"class":72,"line":303},[160,1303,1304],{"class":195},"            f",[160,1306,977],{"class":169},[160,1308,981],{"class":980},[160,1310,1311],{"class":199},"cell.coordinate",[160,1313,993],{"class":980},[160,1315,1316],{"class":169}," is not a formula: ",[160,1318,981],{"class":980},[160,1320,1321],{"class":199},"cell.value",[160,1323,1324],{"class":195},"!r",[160,1326,993],{"class":980},[160,1328,1033],{"class":169},[160,1330,1331],{"class":72,"line":323},[160,1332,1333],{"class":199},"        )\n",[160,1335,1336],{"class":72,"line":328},[160,1337,213],{"emptyLinePlaceholder":212},[160,1339,1340,1342,1345,1348,1351,1353,1356,1358,1360,1363,1366,1369,1372,1374],{"class":72,"line":345},[160,1341,410],{"class":292},[160,1343,1344],{"class":199},"(",[160,1346,1347],{"class":169},"\"formulas present:\"",[160,1349,1350],{"class":199},", [c.value ",[160,1352,941],{"class":195},[160,1354,1355],{"class":199}," c ",[160,1357,947],{"class":195},[160,1359,568],{"class":199},[160,1361,1362],{"class":169},"\"D\"",[160,1364,1365],{"class":199},"][",[160,1367,1368],{"class":292},"1",[160,1370,1371],{"class":199},":",[160,1373,293],{"class":292},[160,1375,1376],{"class":199},"]])\n",[29,1378,38,1384,38,1387,38,1390,38,1395,38,1399,38,1405,38,1410,38,1414,38,1419,38,1423,38,1426,38,1430,38,1433,38,1436,38,1440,38,1443,38,1447,38,1451,38,1454,38,1457],{"viewBox":1379,"role":32,"ariaLabelledBy":1380,"xmlns":36,"style":1383},"4 32 712 163",[1381,1382],"wf-verify-t","wf-verify-d","width:100%;max-width:712px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[40,1385,1386],{"id":1381},"A three-step check that a written formula is really there",[44,1388,1389],{"id":1382},"Three stages left to right: write the formula and save, reload the workbook in the default mode and confirm the cell value is a string starting with equals, then open once in Excel if a number is needed downstream.",[48,1391],{"x":293,"y":1392,"width":1393,"height":1394,"fill":53},"32","712","163",[48,1396],{"x":85,"y":654,"width":1397,"height":1398,"rx":644,"fill":89,"stroke":62,"style":63},"200","96",[65,1400,1404],{"x":1401,"y":1402,"style":1403},"120","80","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","1 · write",[65,1406,1409],{"x":1401,"y":1407,"style":1408},"104","font-size:11.5px;fill:var(--muted,#5b6780);text-anchor:middle","assign the string, save",[65,1411,1413],{"x":1401,"y":1412,"style":1408},"124","wb.save(path)",[72,1415],{"x1":1416,"y1":1398,"x2":1417,"y2":1398,"stroke":1418,"style":63},"224","256","var(--muted,#5b6780)",[78,1420],{"points":1421,"fill":1422},"260,96 248,90 248,102","#5b6780",[48,1424],{"x":1425,"y":654,"width":1397,"height":1398,"rx":644,"fill":61,"stroke":90},"262",[65,1427,1429],{"x":1428,"y":1402,"style":1403},"362","2 · assert",[65,1431,1432],{"x":1428,"y":1407,"style":1408},"reload, default mode",[65,1434,1435],{"x":1428,"y":1412,"style":1408},"value.startswith(\"=\")",[72,1437],{"x1":1438,"y1":1398,"x2":1439,"y2":1398,"stroke":1418,"style":63},"466","498",[78,1441],{"points":1442,"fill":1422},"502,96 490,90 490,102",[48,1444],{"x":1445,"y":654,"width":1446,"height":1398,"rx":644,"fill":110,"stroke":111,"style":63},"504","196",[65,1448,1450],{"x":1449,"y":1402,"style":680},"602","3 · calculate",[65,1452,1453],{"x":1449,"y":1407,"style":1408},"only if a downstream job",[65,1455,1456],{"x":1449,"y":1412,"style":1408},"needs the number",[65,1458,1461],{"x":1459,"y":1460,"style":100},"360","176","Steps 1 and 2 need no spreadsheet application; step 3 does.",[10,1463,1464,1465,1469],{},"If a later job needs the number rather than the formula, either open the file once in a spreadsheet application, or compute the figure in Python and write a literal instead. ",[23,1466,1468],{"href":1467},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fread-formula-results-with-openpyxl-data-only\u002F","Reading formula results with data_only"," goes through that decision in detail.",[143,1471,1473],{"id":1472},"step-5-add-formulas-to-a-workbook-that-already-exists","Step 5: add formulas to a workbook that already exists",[10,1475,1476],{},"Most real jobs do not create the file — they open last month's workbook, append the new rows and extend the formulas to cover them. Loading in the default mode keeps every existing formula intact, so you only write the new ones:",[151,1478,1480],{"className":186,"code":1479,"language":188,"meta":156,"style":156},"from openpyxl import load_workbook\n\nnew_rows = [(\"D-400\", 5, 8.75), (\"E-500\", 3, 22.40)]\n\nwb = load_workbook(\"orders_indexed.xlsx\")\nws = wb[\"Sheet\"]\n\n# The old total row sits at the bottom; drop it before appending data\nlast_data_row = max(\n    cell.row for cell in ws[\"A\"] if cell.value and str(cell.value).startswith((\"A-\", \"B-\", \"C-\"))\n)\nws.delete_rows(last_data_row + 1, ws.max_row - last_data_row)\n\nfor sku, qty, price in new_rows:\n    ws.append([sku, qty, price])\n    r = ws.max_row\n    ws[f\"D{r}\"] = f\"=B{r}*C{r}\"\n\ntotal_row = ws.max_row + 2\nws[f\"C{total_row}\"] = \"Total\"\nws[f\"D{total_row}\"] = f\"=SUM(D2:D{ws.max_row - 2})\"\n\nwb.save(\"orders_extended.xlsx\")\n",[14,1481,1482,1492,1496,1535,1539,1551,1564,1568,1573,1586,1633,1637,1655,1659,1671,1676,1684,1727,1731,1746,1770,1807,1811],{"__ignoreMap":156},[160,1483,1484,1486,1488,1490],{"class":72,"line":162},[160,1485,196],{"class":195},[160,1487,200],{"class":199},[160,1489,203],{"class":195},[160,1491,521],{"class":199},[160,1493,1494],{"class":72,"line":209},[160,1495,213],{"emptyLinePlaceholder":212},[160,1497,1498,1501,1503,1505,1508,1510,1513,1515,1518,1520,1523,1525,1528,1530,1533],{"class":72,"line":216},[160,1499,1500],{"class":199},"new_rows ",[160,1502,20],{"class":195},[160,1504,790],{"class":199},[160,1506,1507],{"class":169},"\"D-400\"",[160,1509,263],{"class":199},[160,1511,1512],{"class":292},"5",[160,1514,263],{"class":199},[160,1516,1517],{"class":292},"8.75",[160,1519,803],{"class":199},[160,1521,1522],{"class":169},"\"E-500\"",[160,1524,263],{"class":199},[160,1526,1527],{"class":292},"3",[160,1529,263],{"class":199},[160,1531,1532],{"class":292},"22.40",[160,1534,831],{"class":199},[160,1536,1537],{"class":72,"line":227},[160,1538,213],{"emptyLinePlaceholder":212},[160,1540,1541,1543,1545,1547,1549],{"class":72,"line":238},[160,1542,219],{"class":199},[160,1544,20],{"class":195},[160,1546,534],{"class":199},[160,1548,1145],{"class":169},[160,1550,404],{"class":199},[160,1552,1553,1555,1557,1559,1562],{"class":72,"line":249},[160,1554,230],{"class":199},[160,1556,20],{"class":195},[160,1558,548],{"class":199},[160,1560,1561],{"class":169},"\"Sheet\"",[160,1563,554],{"class":199},[160,1565,1566],{"class":72,"line":254},[160,1567,213],{"emptyLinePlaceholder":212},[160,1569,1570],{"class":72,"line":282},[160,1571,1572],{"class":421},"# The old total row sits at the bottom; drop it before appending data\n",[160,1574,1575,1578,1580,1583],{"class":72,"line":303},[160,1576,1577],{"class":199},"last_data_row ",[160,1579,20],{"class":195},[160,1581,1582],{"class":292}," max",[160,1584,1585],{"class":199},"(\n",[160,1587,1588,1591,1593,1595,1597,1599,1602,1604,1606,1609,1611,1614,1617,1620,1622,1625,1627,1630],{"class":72,"line":323},[160,1589,1590],{"class":199},"    cell.row ",[160,1592,941],{"class":195},[160,1594,1266],{"class":199},[160,1596,947],{"class":195},[160,1598,568],{"class":199},[160,1600,1601],{"class":169},"\"A\"",[160,1603,337],{"class":199},[160,1605,577],{"class":195},[160,1607,1608],{"class":199}," cell.value ",[160,1610,1290],{"class":195},[160,1612,1613],{"class":292}," str",[160,1615,1616],{"class":199},"(cell.value).startswith((",[160,1618,1619],{"class":169},"\"A-\"",[160,1621,263],{"class":199},[160,1623,1624],{"class":169},"\"B-\"",[160,1626,263],{"class":199},[160,1628,1629],{"class":169},"\"C-\"",[160,1631,1632],{"class":199},"))\n",[160,1634,1635],{"class":72,"line":328},[160,1636,404],{"class":199},[160,1638,1639,1642,1644,1646,1649,1652],{"class":72,"line":345},[160,1640,1641],{"class":199},"ws.delete_rows(last_data_row ",[160,1643,878],{"class":195},[160,1645,881],{"class":292},[160,1647,1648],{"class":199},", ws.max_row ",[160,1650,1651],{"class":195},"-",[160,1653,1654],{"class":199}," last_data_row)\n",[160,1656,1657],{"class":72,"line":360},[160,1658,213],{"emptyLinePlaceholder":212},[160,1660,1661,1663,1666,1668],{"class":72,"line":375},[160,1662,941],{"class":195},[160,1664,1665],{"class":199}," sku, qty, price ",[160,1667,947],{"class":195},[160,1669,1670],{"class":199}," new_rows:\n",[160,1672,1673],{"class":72,"line":390},[160,1674,1675],{"class":199},"    ws.append([sku, qty, price])\n",[160,1677,1678,1680,1682],{"class":72,"line":395},[160,1679,960],{"class":199},[160,1681,20],{"class":195},[160,1683,965],{"class":199},[160,1685,1686,1688,1690,1693,1695,1697,1699,1701,1703,1705,1707,1710,1712,1714,1716,1719,1721,1723,1725],{"class":72,"line":407},[160,1687,971],{"class":199},[160,1689,974],{"class":195},[160,1691,1692],{"class":169},"\"D",[160,1694,981],{"class":980},[160,1696,990],{"class":199},[160,1698,993],{"class":980},[160,1700,977],{"class":169},[160,1702,337],{"class":199},[160,1704,20],{"class":195},[160,1706,1002],{"class":195},[160,1708,1709],{"class":169},"\"=B",[160,1711,981],{"class":980},[160,1713,990],{"class":199},[160,1715,993],{"class":980},[160,1717,1718],{"class":169},"*C",[160,1720,981],{"class":980},[160,1722,990],{"class":199},[160,1724,993],{"class":980},[160,1726,1033],{"class":169},[160,1728,1729],{"class":72,"line":968},[160,1730,213],{"emptyLinePlaceholder":212},[160,1732,1733,1736,1738,1741,1743],{"class":72,"line":1036},[160,1734,1735],{"class":199},"total_row ",[160,1737,20],{"class":195},[160,1739,1740],{"class":199}," ws.max_row ",[160,1742,878],{"class":195},[160,1744,1745],{"class":292}," 2\n",[160,1747,1748,1750,1752,1755,1757,1760,1762,1764,1766,1768],{"class":72,"line":1041},[160,1749,331],{"class":199},[160,1751,974],{"class":195},[160,1753,1754],{"class":169},"\"C",[160,1756,981],{"class":980},[160,1758,1759],{"class":199},"total_row",[160,1761,993],{"class":980},[160,1763,977],{"class":169},[160,1765,337],{"class":199},[160,1767,20],{"class":195},[160,1769,372],{"class":169},[160,1771,1772,1774,1776,1778,1780,1782,1784,1786,1788,1790,1792,1795,1797,1799,1801,1803,1805],{"class":72,"line":1074},[160,1773,331],{"class":199},[160,1775,974],{"class":195},[160,1777,1692],{"class":169},[160,1779,981],{"class":980},[160,1781,1759],{"class":199},[160,1783,993],{"class":980},[160,1785,977],{"class":169},[160,1787,337],{"class":199},[160,1789,20],{"class":195},[160,1791,1002],{"class":195},[160,1793,1794],{"class":169},"\"=SUM(D2:D",[160,1796,981],{"class":980},[160,1798,1056],{"class":199},[160,1800,1651],{"class":195},[160,1802,1061],{"class":292},[160,1804,993],{"class":980},[160,1806,1132],{"class":169},[160,1808,1809],{"class":72,"line":1135},[160,1810,213],{"emptyLinePlaceholder":212},[160,1812,1813,1815,1818],{"class":72,"line":1140},[160,1814,398],{"class":199},[160,1816,1817],{"class":169},"\"orders_extended.xlsx\"",[160,1819,404],{"class":199},[10,1821,1822],{},"The delete-then-rebuild pattern is deliberate. Appending under an existing total row leaves the total pointing at a range that no longer covers the data, which is the single most common cause of a report that quietly under-reports. Removing the summary block, appending, and rewriting the summary is both simpler and safer than trying to patch the range inside the old formula string.",[10,1824,1825],{},"If the workbook you are extending was produced by another team, load it once and check what is actually in the summary cells before you assume the layout:",[151,1827,1829],{"className":186,"code":1828,"language":188,"meta":156,"style":156},"from openpyxl import load_workbook\n\nwb = load_workbook(\"orders_indexed.xlsx\")\nws = wb.active\nformulas = {\n    cell.coordinate: cell.value\n    for row in ws.iter_rows()\n    for cell in row\n    if isinstance(cell.value, str) and cell.value.startswith(\"=\")\n}\nfor coord, formula in formulas.items():\n    print(coord, formula)\n",[14,1830,1831,1841,1845,1857,1865,1875,1880,1891,1902,1923,1928,1940],{"__ignoreMap":156},[160,1832,1833,1835,1837,1839],{"class":72,"line":162},[160,1834,196],{"class":195},[160,1836,200],{"class":199},[160,1838,203],{"class":195},[160,1840,521],{"class":199},[160,1842,1843],{"class":72,"line":209},[160,1844,213],{"emptyLinePlaceholder":212},[160,1846,1847,1849,1851,1853,1855],{"class":72,"line":216},[160,1848,219],{"class":199},[160,1850,20],{"class":195},[160,1852,534],{"class":199},[160,1854,1145],{"class":169},[160,1856,404],{"class":199},[160,1858,1859,1861,1863],{"class":72,"line":227},[160,1860,230],{"class":199},[160,1862,20],{"class":195},[160,1864,235],{"class":199},[160,1866,1867,1870,1872],{"class":72,"line":238},[160,1868,1869],{"class":199},"formulas ",[160,1871,20],{"class":195},[160,1873,1874],{"class":199}," {\n",[160,1876,1877],{"class":72,"line":249},[160,1878,1879],{"class":199},"    cell.coordinate: cell.value\n",[160,1881,1882,1884,1886,1888],{"class":72,"line":254},[160,1883,1263],{"class":195},[160,1885,944],{"class":199},[160,1887,947],{"class":195},[160,1889,1890],{"class":199}," ws.iter_rows()\n",[160,1892,1893,1895,1897,1899],{"class":72,"line":282},[160,1894,1263],{"class":195},[160,1896,1266],{"class":199},[160,1898,947],{"class":195},[160,1900,1901],{"class":199}," row\n",[160,1903,1904,1907,1909,1911,1913,1915,1917,1919,1921],{"class":72,"line":303},[160,1905,1906],{"class":195},"    if",[160,1908,1279],{"class":292},[160,1910,1282],{"class":199},[160,1912,1285],{"class":292},[160,1914,875],{"class":199},[160,1916,1290],{"class":195},[160,1918,1293],{"class":199},[160,1920,1296],{"class":169},[160,1922,404],{"class":199},[160,1924,1925],{"class":72,"line":323},[160,1926,1927],{"class":199},"}\n",[160,1929,1930,1932,1935,1937],{"class":72,"line":328},[160,1931,941],{"class":195},[160,1933,1934],{"class":199}," coord, formula ",[160,1936,947],{"class":195},[160,1938,1939],{"class":199}," formulas.items():\n",[160,1941,1942,1945],{"class":72,"line":345},[160,1943,1944],{"class":292},"    print",[160,1946,1947],{"class":199},"(coord, formula)\n",[10,1949,1950],{},"That inventory is a good thing to log on every run. When a report breaks three months from now, the diff between two runs' formula inventories usually points straight at the change.",[143,1952,1954],{"id":1953},"common-pitfalls-and-gotchas","Common pitfalls and gotchas",[1956,1957,1958,1974],"table",{},[1959,1960,1961],"thead",{},[1962,1963,1964,1968,1971],"tr",{},[1965,1966,1967],"th",{},"Symptom",[1965,1969,1970],{},"Cause",[1965,1972,1973],{},"Fix",[1975,1976,1977,1998,2011,2028,2050,2068],"tbody",{},[1962,1978,1979,1986,1992],{},[1980,1981,1982,1983,1985],"td",{},"Excel shows ",[14,1984,662],{}," as text",[1980,1987,1988,1989],{},"Cell number format is ",[14,1990,1991],{},"@",[1980,1993,1994,1995,1997],{},"Set ",[14,1996,681],{}," before assigning",[1962,1999,2000,2005,2008],{},[1980,2001,2002],{},[14,2003,2004],{},"AttributeError: 'MergedCell' object attribute 'value' is read-only",[1980,2006,2007],{},"Wrote to a non-anchor cell of a merged range",[1980,2009,2010],{},"Write to the top-left cell of the merge",[1962,2012,2013,2019,2022],{},[1980,2014,2015,2018],{},[14,2016,2017],{},"#REF!"," when the file opens",[1980,2020,2021],{},"The reference points outside the used range",[1980,2023,2024,2025,2027],{},"Build references from ",[14,2026,1089],{}," rather than fixed numbers",[1962,2029,2030,2037,2043],{},[1980,2031,2032,2033,2036],{},"Formula reads back as ",[14,2034,2035],{},"None"," in Python",[1980,2038,2039,2040],{},"Loaded with ",[14,2041,2042],{},"data_only=True",[1980,2044,2045,2046,2049],{},"Load without ",[14,2047,2048],{},"data_only"," to see formula text",[1962,2051,2052,2055,2061],{},[1980,2053,2054],{},"Result is right but shows as a percentage",[1980,2056,2057,2058,2060],{},"Destination cell inherited a ",[14,2059,704],{}," format",[1980,2062,2063,2064,2067],{},"Assign ",[14,2065,2066],{},"number_format"," explicitly after writing",[1962,2069,2070,2073,2079],{},[1980,2071,2072],{},"Formula string appears with a leading apostrophe in Excel",[1980,2074,2075,2076],{},"The string was written starting with ",[14,2077,2078],{},"'=",[1980,2080,2081,2082],{},"Strip the apostrophe; start the string at ",[14,2083,20],{},[143,2085,2087],{"id":2086},"performance-and-scale-notes","Performance and scale notes",[10,2089,2090,2091,2094],{},"Formula strings are cheap to write — a few hundred thousand assignments cost far less than the styling around them, because each one is stored as a short string rather than a computed number. The cost lands on the reader: a workbook with 200,000 volatile formulas takes many seconds to open and recalculates on every edit. When a sheet is that large, compute the column in pandas and write literal values, and keep formulas for the handful of summary cells a reader might want to change. ",[14,2092,2093],{},"write_only=True"," mode also accepts formula strings, which lets you stream a large sheet out without holding it in memory; the trade-off is that you can no longer revisit a cell once its row has been written, so every formula has to be built at the moment the row is appended.",[143,2096,2098],{"id":2097},"conclusion","Conclusion",[10,2100,2101,2102,2104,2105,2107,2108,2110],{},"Writing a formula is one assignment of a string beginning with ",[14,2103,20],{},", and openpyxl stores it verbatim. Everything that goes wrong afterwards traces back to three things: a Text number format that stops Excel parsing the string, references built by hand instead of from the row you are writing, and reading the file back in ",[14,2106,2048],{}," mode and finding text-free ",[14,2109,2035],{},"s. Set the format, generate the references, assert on the reloaded file, and the formulas you write will be the formulas your readers see.",[143,2112,2114],{"id":2113},"frequently-asked-questions","Frequently asked questions",[10,2116,2117,2121],{},[2118,2119,2120],"strong",{},"Do I need to escape anything in the formula string?","\nNo. The string is stored verbatim, so write it exactly as you would type it in Excel. Use an f-string or normal concatenation to build the cell references.",[10,2123,2124,2127,2128,2131],{},[2118,2125,2126],{},"Why does Excel display my formula instead of calculating it?","\nThe cell's number format is Text. Set ",[14,2129,2130],{},"cell.number_format = \"General\""," before assigning the formula, or clear the Text format on the range you are writing into.",[10,2133,2134,2137,2138,2141,2142,2145],{},[2118,2135,2136],{},"Can I write a formula to a merged cell?","\nYes, but write it to the top-left cell of the merged range. Assigning to any other cell in the range raises an ",[14,2139,2140],{},"AttributeError"," because those cells are read-only ",[14,2143,2144],{},"MergedCell"," objects.",[10,2147,2148,2151,2152,436],{},[2118,2149,2150],{},"How do I write a formula that references another sheet?","\nPrefix the reference with the sheet name and wrap the name in single quotes if it contains spaces, for example ",[14,2153,2154],{},"='Q1 Sales'!B2",[143,2156,2158],{"id":2157},"related","Related",[10,2160,2161],{},"Up to the parent guide:",[2163,2164,2165],"ul",{},[2166,2167,2168,2170],"li",{},[23,2169,26],{"href":25}," — the full round trip, from writing to freezing.",[10,2172,2173],{},"Related guides:",[2163,2175,2176,2184,2191,2198],{},[2166,2177,2178,2181,2182,436],{},[23,2179,2180],{"href":1467},"Read Formula Results with openpyxl data_only"," — what to do when results come back as ",[14,2183,2035],{},[2166,2185,2186,2190],{},[23,2187,2189],{"href":2188},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fadd-sum-and-average-formulas-with-python\u002F","Add SUM and AVERAGE Formulas with Python"," — totals that track a growing table.",[2166,2192,2193,2197],{},[23,2194,2196],{"href":2195},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcopy-formula-down-a-column-in-openpyxl\u002F","Copy a Formula Down a Column in openpyxl"," — fill a pattern across every data row.",[2166,2199,2200,2204],{},[23,2201,2203],{"href":2202},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002F","Using openpyxl for Excel File Manipulation"," — the workbook, sheet and cell model.",[2206,2207,2208],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}",{"title":156,"searchDepth":209,"depth":209,"links":2210},[2211,2212,2213,2214,2215,2216,2217,2218,2219,2220,2221],{"id":145,"depth":209,"text":146},{"id":179,"depth":209,"text":180},{"id":492,"depth":209,"text":493},{"id":715,"depth":209,"text":716},{"id":1162,"depth":209,"text":1163},{"id":1472,"depth":209,"text":1473},{"id":1953,"depth":209,"text":1954},{"id":2086,"depth":209,"text":2087},{"id":2097,"depth":209,"text":2098},{"id":2113,"depth":209,"text":2114},{"id":2157,"depth":209,"text":2158},"2026-08-01","Assign a formula string to a cell with openpyxl, avoid the Text-format trap that shows the formula instead of a result, and verify what actually landed in the file.","md",[2226,2228,2230,2232],{"q":2120,"a":2227},"No. The string is stored verbatim, so write it exactly as you would type it in Excel. Use an f-string or normal concatenation to build the cell references.",{"q":2126,"a":2229},"The cell's number format is Text. Set cell.number_format = \"General\" before assigning the formula, or clear the Text format on the range you are writing into.",{"q":2136,"a":2231},"Yes, but write it to the top-left cell of the merged range. Assigning to any other cell in the range raises an AttributeError because those cells are read-only MergedCell objects.",{"q":2150,"a":2233},"Prefix the reference with the sheet name and wrap the name in single quotes if it contains spaces, for example ='Q1 Sales'!B2.",{"breadcrumb":2235},[2236,2239,2242,2243],{"name":2237,"item":2238},"Home","\u002F",{"name":2240,"item":2241},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":26,"item":25},{"name":5,"item":2244},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formula-to-excel-cell-with-openpyxl\u002F","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formula-to-excel-cell-with-openpyxl",{"title":5,"description":2247},"Write =SUM(...) and other formulas from Python with openpyxl: the one-line assignment, the number-format trap, translating column indexes, and verifying the saved file.","write-formula-to-excel-cell-with-openpyxl","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formula-to-excel-cell-with-openpyxl\u002Findex","how-to","xzyMgQJEnSiQsIiz2xbvgpjeLK6ZYOCnQUmUe98A_p0",[2253,2256],{"title":2180,"path":2254,"stem":2255,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fread-formula-results-with-openpyxl-data-only","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fread-formula-results-with-openpyxl-data-only\u002Findex",{"title":2257,"path":2258,"stem":2259,"children":-1},"Working with Multiple Excel Sheets in Python","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python","getting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Findex",1785584462714]