[{"data":1,"prerenderedAt":2108},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formulas-that-reference-other-sheets-with-openpyxl":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formulas-that-reference-other-sheets-with-openpyxl":2073,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formulas-that-reference-other-sheets-with-openpyxl":2082,"children:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formulas-that-reference-other-sheets-with-openpyxl":2087,"related:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formulas-that-reference-other-sheets-with-openpyxl":2088},{"id":4,"title":5,"body":6,"dateModified":2038,"datePublished":2038,"description":2039,"extension":2040,"faq":2041,"keyword":2050,"kind":2051,"meta":2052,"navigation":210,"path":2061,"seo":2062,"slug":2065,"stem":2066,"topics":2067,"type":2071,"__hash__":2072},"docs\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formulas-that-reference-other-sheets-with-openpyxl\u002Findex.md","Write Formulas That Reference Other Sheets with openpyxl",{"type":7,"value":8,"toc":2024},"minimark",[9,32,139,144,169,377,381,388,474,477,499,525,529,536,723,736,923,927,942,945,1077,1088,1092,1099,1134,1137,1460,1470,1658,1662,1665,1853,1880,1884,1894,1898,1938,1942,1945,1949,1959,1963,1969,1975,1981,1987,1991,2020],[10,11,12,13,17,18,21,22,25,26,31],"p",{},"A summary sheet is only as good as its links. ",[14,15,16],"code",{},"=SUM(Sales!B2:B50)"," works; ",[14,19,20],{},"=SUM(Sales Data!B2:B50)"," does not; ",[14,23,24],{},"='Q1'!B2"," fails for a reason that has nothing to do with the quotes; and after a script renames a sheet, formulas that looked fine yesterday point at nothing. Cross-sheet formulas written from Python follow Excel's reference syntax exactly, and openpyxl does none of the bookkeeping Excel does for users. This page, part of ",[27,28,30],"a",{"href":29},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002F","Working with Excel Formulas in Python",", shows the syntax, a helper that gets the quoting right every time, and how to keep formulas working when sheets are renamed. Results were checked by recalculating the files in LibreOffice.",[33,34,42,43,42,47,42,51,42,59,42,66,42,76,42,87,42,92,42,97,42,102,42,109,42,114,42,118,42,123,42,130,42,135],"svg",{"viewBox":35,"role":36,"ariaLabelledBy":37,"xmlns":40,"style":41},"64 0 632 363","img",[38,39],"xs-anatomy-t","xs-anatomy-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:632px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[44,45,46],"title",{"id":38},"The parts of a cross-sheet reference",[48,49,50],"desc",{"id":39},"A cross-sheet reference built from three parts. First the sheet name, quoted with apostrophes when it contains spaces or punctuation, with any apostrophe inside doubled. Then an exclamation mark. Then the cell or range address, such as B2:B50. Together: =SUM('Sales Data'!B2:B50).",[52,53],"rect",{"x":54,"y":55,"width":56,"height":57,"fill":58},"64","0","632","363","#ffffff",[60,61,65],"text",{"x":62,"y":63,"style":64},"380.0","31.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","=SUM('Sales Data'!B2:B50)",[52,67],{"x":68,"y":69,"width":70,"height":71,"rx":72,"fill":73,"stroke":74,"style":75},"80.0","48.0","600.0","93.0","12","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[77,78,80],"g",{"transform":79},"translate(368.0,61.0) scale(1.000)",[81,82],"path",{"d":83,"fill":84,"stroke":85,"style":86},"M4 4h16v16H4z M4 9h16 M4 14h16 M10 4v16","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[60,88,91],{"x":62,"y":89,"style":90},"107.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","'Sales Data'",[60,93,96],{"x":62,"y":94,"style":95},"127.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","sheet name, quoted; apostrophes inside are doubled",[52,98],{"x":68,"y":99,"width":70,"height":71,"rx":72,"fill":100,"stroke":101,"style":75},"151.0","#e7ebef","var(--line,#cdd5e6)",[77,103,105],{"transform":104},"translate(368.0,164.0) scale(1.000)",[81,106],{"d":107,"fill":84,"stroke":108,"style":86},"M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1 M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1","var(--text,#172033)",[60,110,113],{"x":62,"y":111,"style":112},"210.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","!",[60,115,117],{"x":62,"y":116,"style":95},"230.0","separates the sheet from the address",[52,119],{"x":68,"y":120,"width":70,"height":71,"rx":72,"fill":121,"stroke":122,"style":75},"254.0","#d9f4f1","var(--teal,#0f9488)",[77,124,126],{"transform":125},"translate(368.0,267.0) scale(1.000)",[81,127],{"d":128,"fill":84,"stroke":129,"style":86},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14","var(--teal-ink,#0b6157)",[60,131,134],{"x":62,"y":132,"style":133},"313.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","B2:B50",[60,136,138],{"x":62,"y":137,"style":95},"333.0","cell, range, whole column or defined name",[140,141,143],"h2",{"id":142},"prerequisites","Prerequisites",[145,146,151],"pre",{"className":147,"code":148,"language":149,"meta":150,"style":150},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[14,152,153],{"__ignoreMap":150},[154,155,158,162,166],"span",{"class":156,"line":157},"line",1,[154,159,161],{"class":160},"sMTad","pip",[154,163,165],{"class":164},"srMev"," install",[154,167,168],{"class":164}," openpyxl\n",[145,170,174],{"className":171,"code":172,"language":173,"meta":150,"style":150},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook, load_workbook\nfrom openpyxl.utils import quote_sheetname\n\nwb = Workbook()\nsummary = wb.active\nsummary.title = \"Summary\"\ndata = wb.create_sheet(\"Sales Data\")\ndata.append([\"region\", \"revenue\"])\nfor region, revenue in [(\"North\", 1200), (\"South\", 950), (\"East\", 700)]:\n    data.append([region, revenue])\nwb.create_sheet(\"O'Brien\")[\"A1\"] = 5\nwb.create_sheet(\"Q1\")[\"A1\"] = 7\n","python",[14,175,176,192,205,212,224,235,246,263,281,329,335,358],{"__ignoreMap":150},[154,177,178,182,186,189],{"class":156,"line":157},[154,179,181],{"class":180},"s-kum","from",[154,183,185],{"class":184},"skGVy"," openpyxl ",[154,187,188],{"class":180},"import",[154,190,191],{"class":184}," Workbook, load_workbook\n",[154,193,195,197,200,202],{"class":156,"line":194},2,[154,196,181],{"class":180},[154,198,199],{"class":184}," openpyxl.utils ",[154,201,188],{"class":180},[154,203,204],{"class":184}," quote_sheetname\n",[154,206,208],{"class":156,"line":207},3,[154,209,211],{"emptyLinePlaceholder":210},true,"\n",[154,213,215,218,221],{"class":156,"line":214},4,[154,216,217],{"class":184},"wb ",[154,219,220],{"class":180},"=",[154,222,223],{"class":184}," Workbook()\n",[154,225,227,230,232],{"class":156,"line":226},5,[154,228,229],{"class":184},"summary ",[154,231,220],{"class":180},[154,233,234],{"class":184}," wb.active\n",[154,236,238,241,243],{"class":156,"line":237},6,[154,239,240],{"class":184},"summary.title ",[154,242,220],{"class":180},[154,244,245],{"class":164}," \"Summary\"\n",[154,247,249,252,254,257,260],{"class":156,"line":248},7,[154,250,251],{"class":184},"data ",[154,253,220],{"class":180},[154,255,256],{"class":184}," wb.create_sheet(",[154,258,259],{"class":164},"\"Sales Data\"",[154,261,262],{"class":184},")\n",[154,264,266,269,272,275,278],{"class":156,"line":265},8,[154,267,268],{"class":184},"data.append([",[154,270,271],{"class":164},"\"region\"",[154,273,274],{"class":184},", ",[154,276,277],{"class":164},"\"revenue\"",[154,279,280],{"class":184},"])\n",[154,282,284,287,290,293,296,299,301,305,308,311,313,316,318,321,323,326],{"class":156,"line":283},9,[154,285,286],{"class":180},"for",[154,288,289],{"class":184}," region, revenue ",[154,291,292],{"class":180},"in",[154,294,295],{"class":184}," [(",[154,297,298],{"class":164},"\"North\"",[154,300,274],{"class":184},[154,302,304],{"class":303},"sP0c6","1200",[154,306,307],{"class":184},"), (",[154,309,310],{"class":164},"\"South\"",[154,312,274],{"class":184},[154,314,315],{"class":303},"950",[154,317,307],{"class":184},[154,319,320],{"class":164},"\"East\"",[154,322,274],{"class":184},[154,324,325],{"class":303},"700",[154,327,328],{"class":184},")]:\n",[154,330,332],{"class":156,"line":331},10,[154,333,334],{"class":184},"    data.append([region, revenue])\n",[154,336,338,341,344,347,350,353,355],{"class":156,"line":337},11,[154,339,340],{"class":184},"wb.create_sheet(",[154,342,343],{"class":164},"\"O'Brien\"",[154,345,346],{"class":184},")[",[154,348,349],{"class":164},"\"A1\"",[154,351,352],{"class":184},"] ",[154,354,220],{"class":180},[154,356,357],{"class":303}," 5\n",[154,359,361,363,366,368,370,372,374],{"class":156,"line":360},12,[154,362,340],{"class":184},[154,364,365],{"class":164},"\"Q1\"",[154,367,346],{"class":184},[154,369,349],{"class":164},[154,371,352],{"class":184},[154,373,220],{"class":180},[154,375,376],{"class":303}," 7\n",[140,378,380],{"id":379},"step-1-quote-sheet-names-that-are-not-plain-words","Step 1 — Quote sheet names that are not plain words",[10,382,383,384,387],{},"A sheet name made only of letters, digits and underscores can be written bare: ",[14,385,386],{},"=Sales!B2",". Anything else — a space, a hyphen, an ampersand, a leading digit — needs single quotes around the name. Leave them out and the formula is broken:",[145,389,391],{"className":171,"code":390,"language":173,"meta":150,"style":150},"summary[\"A1\"] = \"Total (quoted)\"\nsummary[\"B1\"] = \"=SUM('Sales Data'!B2:B4)\"      # 2850 when calculated\nsummary[\"A2\"] = \"Total (unquoted)\"\nsummary[\"B2\"] = \"=SUM(Sales Data!B2:B4)\"        # an error: the space ends the name\nprint(summary[\"B1\"].value, summary[\"B2\"].value)\n",[14,392,393,407,425,439,456],{"__ignoreMap":150},[154,394,395,398,400,402,404],{"class":156,"line":157},[154,396,397],{"class":184},"summary[",[154,399,349],{"class":164},[154,401,352],{"class":184},[154,403,220],{"class":180},[154,405,406],{"class":164}," \"Total (quoted)\"\n",[154,408,409,411,414,416,418,421],{"class":156,"line":194},[154,410,397],{"class":184},[154,412,413],{"class":164},"\"B1\"",[154,415,352],{"class":184},[154,417,220],{"class":180},[154,419,420],{"class":164}," \"=SUM('Sales Data'!B2:B4)\"",[154,422,424],{"class":423},"s-wDw","      # 2850 when calculated\n",[154,426,427,429,432,434,436],{"class":156,"line":207},[154,428,397],{"class":184},[154,430,431],{"class":164},"\"A2\"",[154,433,352],{"class":184},[154,435,220],{"class":180},[154,437,438],{"class":164}," \"Total (unquoted)\"\n",[154,440,441,443,446,448,450,453],{"class":156,"line":214},[154,442,397],{"class":184},[154,444,445],{"class":164},"\"B2\"",[154,447,352],{"class":184},[154,449,220],{"class":180},[154,451,452],{"class":164}," \"=SUM(Sales Data!B2:B4)\"",[154,454,455],{"class":423},"        # an error: the space ends the name\n",[154,457,458,461,464,466,469,471],{"class":156,"line":226},[154,459,460],{"class":303},"print",[154,462,463],{"class":184},"(summary[",[154,465,413],{"class":164},[154,467,468],{"class":184},"].value, summary[",[154,470,445],{"class":164},[154,472,473],{"class":184},"].value)\n",[10,475,476],{},"An apostrophe inside a sheet name is doubled within the quotes, the same way a quote is doubled inside an Excel text string:",[145,478,480],{"className":171,"code":479,"language":173,"meta":150,"style":150},"summary[\"B3\"] = \"='O''Brien'!A1\"                 # 5 when calculated\n",[14,481,482],{"__ignoreMap":150},[154,483,484,486,489,491,493,496],{"class":156,"line":157},[154,485,397],{"class":184},[154,487,488],{"class":164},"\"B3\"",[154,490,352],{"class":184},[154,492,220],{"class":180},[154,494,495],{"class":164}," \"='O''Brien'!A1\"",[154,497,498],{"class":423},"                 # 5 when calculated\n",[10,500,501,502,274,505,274,508,511,512,515,516,519,520,524],{},"There is one more case that catches people: names that look like cell addresses. ",[14,503,504],{},"Q1",[14,506,507],{},"A1",[14,509,510],{},"FY2026"," and ",[14,513,514],{},"R1C1"," are all valid addresses in one notation or another, and a bare ",[14,517,518],{},"=Q1!A1"," does not reliably reach a sheet called Q1. Quoting is harmless for ",[521,522,523],"em",{},"every"," name, so the simplest rule is to always quote.",[140,526,528],{"id":527},"step-2-build-references-with-quote_sheetname-not-f-strings","Step 2 — Build references with quote_sheetname, not f-strings",[10,530,531,532,535],{},"Hand-written quoting is where cross-sheet formulas go wrong in generated workbooks: the sheet names come from data, and one of them eventually contains an apostrophe or a space. openpyxl's ",[14,533,534],{},"quote_sheetname"," always adds the quotes and doubles apostrophes; wrap it in a helper and build every reference through it:",[145,537,539],{"className":171,"code":538,"language":173,"meta":150,"style":150},"def ref(sheet, address):\n    return f\"{quote_sheetname(sheet)}!{address}\"\n\nfor name in [\"Sales Data\", \"O'Brien\", \"Q1\", \"Plain\"]:\n    print(ref(name, \"B2:B4\"))\n\nsummary[\"B4\"] = f\"=SUM({ref('Sales Data', 'B2:B4')})\"\nsummary[\"B5\"] = f\"={ref('Q1', 'A1')}*2\"\nprint(summary[\"B4\"].value, summary[\"B5\"].value)\n",[14,540,541,553,586,590,620,634,638,674,709],{"__ignoreMap":150},[154,542,543,546,550],{"class":156,"line":157},[154,544,545],{"class":180},"def",[154,547,549],{"class":548},"s_Opv"," ref",[154,551,552],{"class":184},"(sheet, address):\n",[154,554,555,558,561,564,568,571,574,576,578,581,583],{"class":156,"line":194},[154,556,557],{"class":180},"    return",[154,559,560],{"class":180}," f",[154,562,563],{"class":164},"\"",[154,565,567],{"class":566},"sSjpA","{",[154,569,570],{"class":184},"quote_sheetname(sheet)",[154,572,573],{"class":566},"}",[154,575,113],{"class":164},[154,577,567],{"class":566},[154,579,580],{"class":184},"address",[154,582,573],{"class":566},[154,584,585],{"class":164},"\"\n",[154,587,588],{"class":156,"line":207},[154,589,211],{"emptyLinePlaceholder":210},[154,591,592,594,597,599,602,604,606,608,610,612,614,617],{"class":156,"line":214},[154,593,286],{"class":180},[154,595,596],{"class":184}," name ",[154,598,292],{"class":180},[154,600,601],{"class":184}," [",[154,603,259],{"class":164},[154,605,274],{"class":184},[154,607,343],{"class":164},[154,609,274],{"class":184},[154,611,365],{"class":164},[154,613,274],{"class":184},[154,615,616],{"class":164},"\"Plain\"",[154,618,619],{"class":184},"]:\n",[154,621,622,625,628,631],{"class":156,"line":226},[154,623,624],{"class":303},"    print",[154,626,627],{"class":184},"(ref(name, ",[154,629,630],{"class":164},"\"B2:B4\"",[154,632,633],{"class":184},"))\n",[154,635,636],{"class":156,"line":237},[154,637,211],{"emptyLinePlaceholder":210},[154,639,640,642,645,647,649,651,654,656,659,661,663,666,669,671],{"class":156,"line":248},[154,641,397],{"class":184},[154,643,644],{"class":164},"\"B4\"",[154,646,352],{"class":184},[154,648,220],{"class":180},[154,650,560],{"class":180},[154,652,653],{"class":164},"\"=SUM(",[154,655,567],{"class":566},[154,657,658],{"class":184},"ref(",[154,660,91],{"class":164},[154,662,274],{"class":184},[154,664,665],{"class":164},"'B2:B4'",[154,667,668],{"class":184},")",[154,670,573],{"class":566},[154,672,673],{"class":164},")\"\n",[154,675,676,678,681,683,685,687,690,692,694,697,699,702,704,706],{"class":156,"line":265},[154,677,397],{"class":184},[154,679,680],{"class":164},"\"B5\"",[154,682,352],{"class":184},[154,684,220],{"class":180},[154,686,560],{"class":180},[154,688,689],{"class":164},"\"=",[154,691,567],{"class":566},[154,693,658],{"class":184},[154,695,696],{"class":164},"'Q1'",[154,698,274],{"class":184},[154,700,701],{"class":164},"'A1'",[154,703,668],{"class":184},[154,705,573],{"class":566},[154,707,708],{"class":164},"*2\"\n",[154,710,711,713,715,717,719,721],{"class":156,"line":283},[154,712,460],{"class":303},[154,714,463],{"class":184},[154,716,644],{"class":164},[154,718,468],{"class":184},[154,720,680],{"class":164},[154,722,473],{"class":184},[10,724,725,726,274,729,274,732,735],{},"Every reference the helper produces is valid whatever the name — ",[14,727,728],{},"'Sales Data'!B2:B4",[14,730,731],{},"'O''Brien'!B2:B4",[14,733,734],{},"'Q1'!B2:B4",". Using it consistently removes a whole class of bug from report code, including the ones that only appear the day a new region called \"Asia-Pacific\" is added.",[33,737,42,743,42,746,42,749,42,754,42,761,42,766,42,772,42,775,42,779,42,782,42,786,42,790,42,796,42,798,42,805,42,807,42,812,42,814,42,818,42,821,42,825,42,827,42,835,42,837,42,842,42,844,42,847,42,850,42,854,42,856,42,861,42,863,42,867,42,869,42,871,42,874,42,877,42,879,42,884,42,886,42,891,42,893,42,896,42,899,42,903,42,905,42,910,42,912,42,917,42,919],{"viewBox":738,"role":36,"ariaLabelledBy":739,"xmlns":40,"style":742},"4 0 752 242",[740,741],"xs-quoting-t","xs-quoting-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[44,744,745],{"id":740},"Sheet names and how they must appear in a formula",[48,747,748],{"id":741},"A table of sheet names. Sales can be written bare or quoted. Sales Data must be quoted because of the space. O'Brien must be quoted with the apostrophe doubled. Q1 should be quoted because it looks like a cell address. 2026 must be quoted because it starts with a digit. quote_sheetname produces a valid form for all of them.",[52,750],{"x":751,"y":55,"width":752,"height":753,"fill":58},"4","752","242",[52,755],{"x":756,"y":757,"width":758,"height":759,"rx":55,"fill":58,"stroke":101,"style":760},"20.0","16.0","95.6","40.0","stroke-width:1px",[52,762],{"x":763,"y":757,"width":764,"height":759,"rx":55,"fill":73,"stroke":74,"style":765},"115.6","208.1","stroke-width:1.2px",[60,767,771],{"x":768,"y":769,"style":770},"219.7","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Bare",[52,773],{"x":774,"y":757,"width":764,"height":759,"rx":55,"fill":73,"stroke":74,"style":765},"323.7",[60,776,778],{"x":777,"y":769,"style":770},"427.8","Quoted",[52,780],{"x":781,"y":757,"width":764,"height":759,"rx":55,"fill":73,"stroke":74,"style":765},"531.9",[60,783,785],{"x":784,"y":769,"style":770},"635.9","quote_sheetname()",[52,787],{"x":756,"y":788,"width":758,"height":789,"rx":55,"fill":100,"stroke":101,"style":760},"56.0","34.0",[60,791,795],{"x":792,"y":793,"style":794},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","Sales",[52,797],{"x":763,"y":788,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[77,799,801],{"transform":800},"translate(210.7,64.0) scale(0.750)",[81,802],{"d":803,"fill":84,"stroke":122,"style":804},"M5 12.5l4.5 4.5L19 7","stroke-width:2.40px;stroke-linecap:round;stroke-linejoin:round",[52,806],{"x":774,"y":788,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[77,808,810],{"transform":809},"translate(418.8,64.0) scale(0.750)",[81,811],{"d":803,"fill":84,"stroke":122,"style":804},[52,813],{"x":781,"y":788,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[60,815,817],{"x":784,"y":816,"style":95},"77.0","'Sales'",[52,819],{"x":756,"y":820,"width":758,"height":789,"rx":55,"fill":100,"stroke":101,"style":760},"90.0",[60,822,824],{"x":792,"y":823,"style":794},"112.0","Sales Data",[52,826],{"x":763,"y":820,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[77,828,830],{"transform":829},"translate(211.7,99.0) scale(0.667)",[81,831],{"d":832,"fill":84,"stroke":833,"style":834},"M6 6l12 12 M18 6L6 18","var(--accent,#f43f8f)","stroke-width:2.70px;stroke-linecap:round;stroke-linejoin:round",[52,836],{"x":774,"y":820,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[77,838,840],{"transform":839},"translate(418.8,98.0) scale(0.750)",[81,841],{"d":803,"fill":84,"stroke":122,"style":804},[52,843],{"x":781,"y":820,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[60,845,91],{"x":784,"y":846,"style":95},"111.0",[52,848],{"x":756,"y":849,"width":758,"height":789,"rx":55,"fill":100,"stroke":101,"style":760},"124.0",[60,851,853],{"x":792,"y":852,"style":794},"146.0","O'Brien",[52,855],{"x":763,"y":849,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[77,857,859],{"transform":858},"translate(211.7,133.0) scale(0.667)",[81,860],{"d":832,"fill":84,"stroke":833,"style":834},[52,862],{"x":774,"y":849,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[60,864,866],{"x":777,"y":865,"style":95},"145.0","'O''Brien'",[52,868],{"x":781,"y":849,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[60,870,866],{"x":784,"y":865,"style":95},[52,872],{"x":756,"y":873,"width":758,"height":789,"rx":55,"fill":100,"stroke":101,"style":760},"158.0",[60,875,504],{"x":792,"y":876,"style":794},"180.0",[52,878],{"x":763,"y":873,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[77,880,882],{"transform":881},"translate(211.7,167.0) scale(0.667)",[81,883],{"d":832,"fill":84,"stroke":833,"style":834},[52,885],{"x":774,"y":873,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[77,887,889],{"transform":888},"translate(418.8,166.0) scale(0.750)",[81,890],{"d":803,"fill":84,"stroke":122,"style":804},[52,892],{"x":781,"y":873,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[60,894,696],{"x":784,"y":895,"style":95},"179.0",[52,897],{"x":756,"y":898,"width":758,"height":789,"rx":55,"fill":100,"stroke":101,"style":760},"192.0",[60,900,902],{"x":792,"y":901,"style":794},"214.0","2026",[52,904],{"x":763,"y":898,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[77,906,908],{"transform":907},"translate(211.7,201.0) scale(0.667)",[81,909],{"d":832,"fill":84,"stroke":833,"style":834},[52,911],{"x":774,"y":898,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[77,913,915],{"transform":914},"translate(418.8,200.0) scale(0.750)",[81,916],{"d":803,"fill":84,"stroke":122,"style":804},[52,918],{"x":781,"y":898,"width":764,"height":789,"rx":55,"fill":58,"stroke":101,"style":760},[60,920,922],{"x":784,"y":921,"style":95},"213.0","'2026'",[140,924,926],{"id":925},"step-3-reference-whole-columns-or-better-defined-names","Step 3 — Reference whole columns, or better, defined names",[10,928,929,930,933,934,937,938,941],{},"For summary formulas over data sheets whose length changes, a whole-column reference avoids updating the range every run: ",[14,931,932],{},"=SUM('Sales Data'!B:B)",". It includes the header row, which ",[14,935,936],{},"SUM"," ignores because it is text, but functions such as ",[14,939,940],{},"COUNTA"," count it, so choose accordingly.",[10,943,944],{},"A defined name is often better still. It gives the range a readable name, keeps the sheet quoting in one place, and survives rows being added if you update the definition when the data is written:",[145,946,948],{"className":171,"code":947,"language":173,"meta":150,"style":150},"from openpyxl.workbook.defined_name import DefinedName\n\nlast = data.max_row\nwb.defined_names[\"Revenue\"] = DefinedName(\"Revenue\", attr_text=f\"{ref('Sales Data', f'$B$2:$B${last}')}\")\nsummary[\"A6\"] = \"Total (named)\"\nsummary[\"B6\"] = \"=SUM(Revenue)\"\nprint(wb.defined_names[\"Revenue\"].attr_text)\n",[14,949,950,962,966,976,1037,1051,1065],{"__ignoreMap":150},[154,951,952,954,957,959],{"class":156,"line":157},[154,953,181],{"class":180},[154,955,956],{"class":184}," openpyxl.workbook.defined_name ",[154,958,188],{"class":180},[154,960,961],{"class":184}," DefinedName\n",[154,963,964],{"class":156,"line":194},[154,965,211],{"emptyLinePlaceholder":210},[154,967,968,971,973],{"class":156,"line":207},[154,969,970],{"class":184},"last ",[154,972,220],{"class":180},[154,974,975],{"class":184}," data.max_row\n",[154,977,978,981,984,986,988,991,993,995,999,1001,1004,1006,1008,1010,1012,1014,1016,1019,1021,1024,1026,1029,1031,1033,1035],{"class":156,"line":214},[154,979,980],{"class":184},"wb.defined_names[",[154,982,983],{"class":164},"\"Revenue\"",[154,985,352],{"class":184},[154,987,220],{"class":180},[154,989,990],{"class":184}," DefinedName(",[154,992,983],{"class":164},[154,994,274],{"class":184},[154,996,998],{"class":997},"sa561","attr_text",[154,1000,220],{"class":180},[154,1002,1003],{"class":180},"f",[154,1005,563],{"class":164},[154,1007,567],{"class":566},[154,1009,658],{"class":184},[154,1011,91],{"class":164},[154,1013,274],{"class":184},[154,1015,1003],{"class":180},[154,1017,1018],{"class":164},"'$B$2:$B$",[154,1020,567],{"class":566},[154,1022,1023],{"class":184},"last",[154,1025,573],{"class":566},[154,1027,1028],{"class":164},"'",[154,1030,668],{"class":184},[154,1032,573],{"class":566},[154,1034,563],{"class":164},[154,1036,262],{"class":184},[154,1038,1039,1041,1044,1046,1048],{"class":156,"line":226},[154,1040,397],{"class":184},[154,1042,1043],{"class":164},"\"A6\"",[154,1045,352],{"class":184},[154,1047,220],{"class":180},[154,1049,1050],{"class":164}," \"Total (named)\"\n",[154,1052,1053,1055,1058,1060,1062],{"class":156,"line":237},[154,1054,397],{"class":184},[154,1056,1057],{"class":164},"\"B6\"",[154,1059,352],{"class":184},[154,1061,220],{"class":180},[154,1063,1064],{"class":164}," \"=SUM(Revenue)\"\n",[154,1066,1067,1069,1072,1074],{"class":156,"line":248},[154,1068,460],{"class":303},[154,1070,1071],{"class":184},"(wb.defined_names[",[154,1073,983],{"class":164},[154,1075,1076],{"class":184},"].attr_text)\n",[10,1078,1079,1082,1083,1087],{},[14,1080,1081],{},"=SUM(Revenue)"," reads better than any range address, and the next person to open the workbook can find the definition in Excel's Name Manager. ",[27,1084,1086],{"href":1085},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcreate-named-range-in-excel-with-openpyxl\u002F","Create a Named Range in Excel with openpyxl"," covers names in depth.",[140,1089,1091],{"id":1090},"step-4-rename-sheets-without-breaking-formulas","Step 4 — Rename sheets without breaking formulas",[10,1093,1094,1095,1098],{},"When a user renames a sheet in Excel, Excel rewrites every formula that refers to it. openpyxl does not — changing ",[14,1096,1097],{},"ws.title"," changes only the tab:",[145,1100,1102],{"className":171,"code":1101,"language":173,"meta":150,"style":150},"wb[\"Q1\"].title = \"Quarter 1\"\nprint(summary[\"B5\"].value)\n# ='Q1'!A1*2 — still the old name; after recalculation this is an error\n",[14,1103,1104,1119,1129],{"__ignoreMap":150},[154,1105,1106,1109,1111,1114,1116],{"class":156,"line":157},[154,1107,1108],{"class":184},"wb[",[154,1110,365],{"class":164},[154,1112,1113],{"class":184},"].title ",[154,1115,220],{"class":180},[154,1117,1118],{"class":164}," \"Quarter 1\"\n",[154,1120,1121,1123,1125,1127],{"class":156,"line":194},[154,1122,460],{"class":303},[154,1124,463],{"class":184},[154,1126,680],{"class":164},[154,1128,473],{"class":184},[154,1130,1131],{"class":156,"line":207},[154,1132,1133],{"class":423},"# ='Q1'!A1*2 — still the old name; after recalculation this is an error\n",[10,1135,1136],{},"Recalculating that workbook turns the cell into an error, because no sheet called Q1 exists any more. Rename through a helper that also rewrites references in every formula and every defined name:",[145,1138,1140],{"className":171,"code":1139,"language":173,"meta":150,"style":150},"import re\n\ndef rename_sheet(wb, old, new):\n    pattern = re.compile(r\"(?\u003C![\\w'])(?:'\" + re.escape(old.replace(\"'\", \"''\")) + r\"'|\" + re.escape(old) + r\")!\")\n    replacement = quote_sheetname(new) + \"!\"\n    wb[old].title = new\n    for ws in wb.worksheets:\n        for row in ws.iter_rows():\n            for cell in row:\n                if cell.data_type == \"f\" and isinstance(cell.value, str):\n                    cell.value = pattern.sub(lambda m: replacement, cell.value)\n    for name, dn in wb.defined_names.items():\n        dn.attr_text = pattern.sub(lambda m: replacement, dn.attr_text)\n\nwb[\"Quarter 1\"].title = \"Q1\"                 # undo the plain rename for the demo\nrename_sheet(wb, \"Q1\", \"Quarter 1\")\nrename_sheet(wb, \"Sales Data\", \"Sales 2026\")\nprint(summary[\"B5\"].value, \"|\", summary[\"B4\"].value, \"|\", wb.defined_names[\"Revenue\"].attr_text)\n",[14,1141,1142,1149,1153,1163,1242,1257,1267,1280,1293,1306,1335,1351,1363,1378,1383,1401,1415,1429],{"__ignoreMap":150},[154,1143,1144,1146],{"class":156,"line":157},[154,1145,188],{"class":180},[154,1147,1148],{"class":184}," re\n",[154,1150,1151],{"class":156,"line":194},[154,1152,211],{"emptyLinePlaceholder":210},[154,1154,1155,1157,1160],{"class":156,"line":207},[154,1156,545],{"class":180},[154,1158,1159],{"class":548}," rename_sheet",[154,1161,1162],{"class":184},"(wb, old, new):\n",[154,1164,1165,1168,1170,1173,1176,1178,1181,1184,1186,1189,1191,1194,1197,1200,1203,1206,1208,1211,1214,1217,1220,1223,1226,1228,1230,1233,1235,1237,1240],{"class":156,"line":214},[154,1166,1167],{"class":184},"    pattern ",[154,1169,220],{"class":180},[154,1171,1172],{"class":184}," re.compile(",[154,1174,1175],{"class":180},"r",[154,1177,563],{"class":164},[154,1179,1180],{"class":180},"(?\u003C!",[154,1182,1183],{"class":303},"[\\w",[154,1185,1028],{"class":566},[154,1187,1188],{"class":303},"]",[154,1190,668],{"class":180},[154,1192,1193],{"class":303},"(?:",[154,1195,1196],{"class":164},"'\"",[154,1198,1199],{"class":180}," +",[154,1201,1202],{"class":184}," re.escape(old.replace(",[154,1204,1205],{"class":164},"\"'\"",[154,1207,274],{"class":184},[154,1209,1210],{"class":164},"\"''\"",[154,1212,1213],{"class":184},")) ",[154,1215,1216],{"class":180},"+",[154,1218,1219],{"class":180}," r",[154,1221,1222],{"class":164},"\"'",[154,1224,1225],{"class":180},"|",[154,1227,563],{"class":164},[154,1229,1199],{"class":180},[154,1231,1232],{"class":184}," re.escape(old) ",[154,1234,1216],{"class":180},[154,1236,1219],{"class":180},[154,1238,1239],{"class":164},"\")!\"",[154,1241,262],{"class":184},[154,1243,1244,1247,1249,1252,1254],{"class":156,"line":226},[154,1245,1246],{"class":184},"    replacement ",[154,1248,220],{"class":180},[154,1250,1251],{"class":184}," quote_sheetname(new) ",[154,1253,1216],{"class":180},[154,1255,1256],{"class":164}," \"!\"\n",[154,1258,1259,1262,1264],{"class":156,"line":237},[154,1260,1261],{"class":184},"    wb[old].title ",[154,1263,220],{"class":180},[154,1265,1266],{"class":184}," new\n",[154,1268,1269,1272,1275,1277],{"class":156,"line":248},[154,1270,1271],{"class":180},"    for",[154,1273,1274],{"class":184}," ws ",[154,1276,292],{"class":180},[154,1278,1279],{"class":184}," wb.worksheets:\n",[154,1281,1282,1285,1288,1290],{"class":156,"line":265},[154,1283,1284],{"class":180},"        for",[154,1286,1287],{"class":184}," row ",[154,1289,292],{"class":180},[154,1291,1292],{"class":184}," ws.iter_rows():\n",[154,1294,1295,1298,1301,1303],{"class":156,"line":283},[154,1296,1297],{"class":180},"            for",[154,1299,1300],{"class":184}," cell ",[154,1302,292],{"class":180},[154,1304,1305],{"class":184}," row:\n",[154,1307,1308,1311,1314,1317,1320,1323,1326,1329,1332],{"class":156,"line":331},[154,1309,1310],{"class":180},"                if",[154,1312,1313],{"class":184}," cell.data_type ",[154,1315,1316],{"class":180},"==",[154,1318,1319],{"class":164}," \"f\"",[154,1321,1322],{"class":180}," and",[154,1324,1325],{"class":303}," isinstance",[154,1327,1328],{"class":184},"(cell.value, ",[154,1330,1331],{"class":303},"str",[154,1333,1334],{"class":184},"):\n",[154,1336,1337,1340,1342,1345,1348],{"class":156,"line":337},[154,1338,1339],{"class":184},"                    cell.value ",[154,1341,220],{"class":180},[154,1343,1344],{"class":184}," pattern.sub(",[154,1346,1347],{"class":180},"lambda",[154,1349,1350],{"class":184}," m: replacement, cell.value)\n",[154,1352,1353,1355,1358,1360],{"class":156,"line":360},[154,1354,1271],{"class":180},[154,1356,1357],{"class":184}," name, dn ",[154,1359,292],{"class":180},[154,1361,1362],{"class":184}," wb.defined_names.items():\n",[154,1364,1366,1369,1371,1373,1375],{"class":156,"line":1365},13,[154,1367,1368],{"class":184},"        dn.attr_text ",[154,1370,220],{"class":180},[154,1372,1344],{"class":184},[154,1374,1347],{"class":180},[154,1376,1377],{"class":184}," m: replacement, dn.attr_text)\n",[154,1379,1381],{"class":156,"line":1380},14,[154,1382,211],{"emptyLinePlaceholder":210},[154,1384,1386,1388,1391,1393,1395,1398],{"class":156,"line":1385},15,[154,1387,1108],{"class":184},[154,1389,1390],{"class":164},"\"Quarter 1\"",[154,1392,1113],{"class":184},[154,1394,220],{"class":180},[154,1396,1397],{"class":164}," \"Q1\"",[154,1399,1400],{"class":423},"                 # undo the plain rename for the demo\n",[154,1402,1404,1407,1409,1411,1413],{"class":156,"line":1403},16,[154,1405,1406],{"class":184},"rename_sheet(wb, ",[154,1408,365],{"class":164},[154,1410,274],{"class":184},[154,1412,1390],{"class":164},[154,1414,262],{"class":184},[154,1416,1418,1420,1422,1424,1427],{"class":156,"line":1417},17,[154,1419,1406],{"class":184},[154,1421,259],{"class":164},[154,1423,274],{"class":184},[154,1425,1426],{"class":164},"\"Sales 2026\"",[154,1428,262],{"class":184},[154,1430,1432,1434,1436,1438,1441,1444,1447,1449,1451,1453,1456,1458],{"class":156,"line":1431},18,[154,1433,460],{"class":303},[154,1435,463],{"class":184},[154,1437,680],{"class":164},[154,1439,1440],{"class":184},"].value, ",[154,1442,1443],{"class":164},"\"|\"",[154,1445,1446],{"class":184},", summary[",[154,1448,644],{"class":164},[154,1450,1440],{"class":184},[154,1452,1443],{"class":164},[154,1454,1455],{"class":184},", wb.defined_names[",[154,1457,983],{"class":164},[154,1459,1076],{"class":184},[10,1461,1462,1463,1465,1466,1469],{},"The pattern matches the old name both quoted and bare, and the lookbehind stops it matching inside a longer name — renaming ",[14,1464,504],{}," must not touch references to ",[14,1467,1468],{},"Q10",". Sheet-scoped defined names, data validation lists and chart references can also mention sheet names; update them the same way if your workbook uses them.",[33,1471,42,1477,42,1480,42,1483,42,1488,42,1492,42,1496,42,1502,42,1506,42,1510,42,1514,42,1518,42,1522,42,1527,42,1529,42,1533,42,1535,42,1538,42,1540,42,1543,42,1546,42,1549,42,1551,42,1555,42,1557,42,1560,42,1564,42,1568,42,1571,42,1574,42,1577,42,1581,42,1584,42,1588,42,1591,42,1593,42,1596,42,1598,42,1600,42,1602,42,1604,42,1606,42,1608,42,1610,42,1612,42,1614,42,1616,42,1618,42,1621,42,1623,42,1627,42,1632,42,1636,42,1640,42,1645,42,1652],{"viewBox":1472,"role":36,"ariaLabelledBy":1473,"xmlns":40,"style":1476},"10 0 739 136",[1474,1475],"xs-rename-t","xs-rename-d","width:100%;max-width:739px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[44,1478,1479],{"id":1474},"Renaming a sheet with and without updating formulas",[48,1481,1482],{"id":1475},"Left: after setting the title of sheet Q1 to Quarter 1, the summary formula still reads ='Q1'!A1*2 and calculates to an error. Right: after rename_sheet, the formula reads ='Quarter 1'!A1*2 and calculates to 14.",[52,1484],{"x":1485,"y":55,"width":1486,"height":1487,"fill":58},"10","739","136",[52,1489],{"x":1490,"y":759,"width":792,"height":1491,"rx":55,"fill":100,"stroke":101,"style":760},"26.9","24.0",[52,1493],{"x":1494,"y":759,"width":1495,"height":1491,"rx":55,"fill":100,"stroke":101,"style":760},"58.9","45.7",[60,1497,1501],{"x":1498,"y":1499,"style":1500},"81.7","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[52,1503],{"x":1504,"y":759,"width":1505,"height":1491,"rx":55,"fill":100,"stroke":101,"style":760},"104.6","90.4",[60,1507,1509],{"x":1508,"y":1499,"style":1500},"149.8","B",[52,1511],{"x":1512,"y":759,"width":1513,"height":1491,"rx":55,"fill":100,"stroke":101,"style":760},"195.0","77.6",[60,1515,1517],{"x":1516,"y":1499,"style":1500},"233.8","C",[52,1519],{"x":1490,"y":1520,"width":792,"height":1521,"rx":55,"fill":100,"stroke":101,"style":760},"64.0","28.0",[60,1523,1526],{"x":1524,"y":1525,"style":1500},"42.9","83.0","1",[52,1528],{"x":1494,"y":1520,"width":1495,"height":1521,"rx":55,"fill":58,"stroke":101,"style":760},[60,1530,1532],{"x":1498,"y":1525,"style":1531},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","cell",[52,1534],{"x":1504,"y":1520,"width":1505,"height":1521,"rx":55,"fill":58,"stroke":101,"style":760},[60,1536,1537],{"x":1508,"y":1525,"style":1531},"formula",[52,1539],{"x":1512,"y":1520,"width":1513,"height":1521,"rx":55,"fill":58,"stroke":101,"style":760},[60,1541,1542],{"x":1516,"y":1525,"style":1531},"result",[52,1544],{"x":1490,"y":1545,"width":792,"height":1521,"rx":55,"fill":100,"stroke":101,"style":760},"92.0",[60,1547,1548],{"x":1524,"y":846,"style":1500},"2",[52,1550],{"x":1494,"y":1545,"width":1495,"height":1521,"rx":55,"fill":58,"stroke":101,"style":760},[60,1552,1554],{"x":1498,"y":846,"style":1553},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","B5",[52,1556],{"x":1504,"y":1545,"width":1505,"height":1521,"rx":55,"fill":58,"stroke":101,"style":760},[60,1558,1559],{"x":1508,"y":846,"style":1553},"='Q1'!A1*2",[52,1561],{"x":1512,"y":1545,"width":1513,"height":1521,"rx":55,"fill":1562,"stroke":833,"style":1563},"#fee8f2","stroke-width:1.5px",[60,1565,1567],{"x":1516,"y":846,"style":1566},"font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","#NAME?",[52,1569],{"x":1570,"y":759,"width":792,"height":1491,"rx":55,"fill":100,"stroke":101,"style":760},"464.9",[52,1572],{"x":1573,"y":759,"width":1495,"height":1491,"rx":55,"fill":100,"stroke":101,"style":760},"496.9",[60,1575,1501],{"x":1576,"y":1499,"style":1500},"519.7",[52,1578],{"x":1579,"y":759,"width":1580,"height":1491,"rx":55,"fill":100,"stroke":101,"style":760},"542.6","130.9",[60,1582,1509],{"x":1583,"y":1499,"style":1500},"608.1",[52,1585],{"x":1586,"y":759,"width":1587,"height":1491,"rx":55,"fill":100,"stroke":101,"style":760},"673.6","59.6",[60,1589,1517],{"x":1590,"y":1499,"style":1500},"703.3",[52,1592],{"x":1570,"y":1520,"width":792,"height":1521,"rx":55,"fill":100,"stroke":101,"style":760},[60,1594,1526],{"x":1595,"y":1525,"style":1500},"480.9",[52,1597],{"x":1573,"y":1520,"width":1495,"height":1521,"rx":55,"fill":58,"stroke":101,"style":760},[60,1599,1532],{"x":1576,"y":1525,"style":1531},[52,1601],{"x":1579,"y":1520,"width":1580,"height":1521,"rx":55,"fill":58,"stroke":101,"style":760},[60,1603,1537],{"x":1583,"y":1525,"style":1531},[52,1605],{"x":1586,"y":1520,"width":1587,"height":1521,"rx":55,"fill":58,"stroke":101,"style":760},[60,1607,1542],{"x":1590,"y":1525,"style":1531},[52,1609],{"x":1570,"y":1545,"width":792,"height":1521,"rx":55,"fill":100,"stroke":101,"style":760},[60,1611,1548],{"x":1595,"y":846,"style":1500},[52,1613],{"x":1573,"y":1545,"width":1495,"height":1521,"rx":55,"fill":58,"stroke":101,"style":760},[60,1615,1554],{"x":1576,"y":846,"style":1553},[52,1617],{"x":1579,"y":1545,"width":1580,"height":1521,"rx":55,"fill":58,"stroke":101,"style":760},[60,1619,1620],{"x":1583,"y":846,"style":1553},"='Quarter 1'!A1*2",[52,1622],{"x":1586,"y":1545,"width":1587,"height":1521,"rx":55,"fill":121,"stroke":122,"style":1563},[60,1624,1626],{"x":1590,"y":846,"style":1625},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","14",[60,1628,1631],{"x":1508,"y":1629,"style":1630},"29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","ws.title = \"Quarter 1\"",[60,1633,1635],{"x":1634,"y":1629,"style":1630},"599.0","rename_sheet(wb, \"Q1\", \"Quarter 1\")",[156,1637],{"x1":1638,"y1":68,"x2":1639,"y2":68,"stroke":74,"style":75},"282.7","450.9",[1641,1642],"polygon",{"points":1643,"fill":1644},"454.9,80.0 445.9,85.0 445.9,75.0","#5b5cf0",[52,1646],{"x":1647,"y":1648,"width":1649,"height":1650,"rx":1651,"fill":58,"stroke":101,"style":765},"307.9","47.0","121.8","22.0","11",[60,1653,1657],{"x":1654,"y":1655,"style":1656},"368.8","62.0","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","rename_sheet()",[140,1659,1661],{"id":1660},"step-5-check-the-result-by-recalculating","Step 5 — Check the result by recalculating",[10,1663,1664],{},"A cross-sheet mistake does not raise anything in Python; it only shows as an error value once the workbook is calculated. Recalculate a saved copy and read the results back as a final check:",[145,1666,1668],{"className":171,"code":1667,"language":173,"meta":150,"style":150},"import subprocess\n\nwb.save(\"linked.xlsx\")\nsubprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"xlsx\", \"--outdir\", \"calc\", \"linked.xlsx\"],\n               check=True, capture_output=True, timeout=120)\ncalc = load_workbook(\"calc\u002Flinked.xlsx\", data_only=True)[\"Summary\"]\nprint({f\"B{r}\": calc[f\"B{r}\"].value for r in range(1, 7)})\n",[14,1669,1670,1677,1681,1691,1731,1762,1792],{"__ignoreMap":150},[154,1671,1672,1674],{"class":156,"line":157},[154,1673,188],{"class":180},[154,1675,1676],{"class":184}," subprocess\n",[154,1678,1679],{"class":156,"line":194},[154,1680,211],{"emptyLinePlaceholder":210},[154,1682,1683,1686,1689],{"class":156,"line":207},[154,1684,1685],{"class":184},"wb.save(",[154,1687,1688],{"class":164},"\"linked.xlsx\"",[154,1690,262],{"class":184},[154,1692,1693,1696,1699,1701,1704,1706,1709,1711,1714,1716,1719,1721,1724,1726,1728],{"class":156,"line":214},[154,1694,1695],{"class":184},"subprocess.run([",[154,1697,1698],{"class":164},"\"soffice\"",[154,1700,274],{"class":184},[154,1702,1703],{"class":164},"\"--headless\"",[154,1705,274],{"class":184},[154,1707,1708],{"class":164},"\"--convert-to\"",[154,1710,274],{"class":184},[154,1712,1713],{"class":164},"\"xlsx\"",[154,1715,274],{"class":184},[154,1717,1718],{"class":164},"\"--outdir\"",[154,1720,274],{"class":184},[154,1722,1723],{"class":164},"\"calc\"",[154,1725,274],{"class":184},[154,1727,1688],{"class":164},[154,1729,1730],{"class":184},"],\n",[154,1732,1733,1736,1738,1741,1743,1746,1748,1750,1752,1755,1757,1760],{"class":156,"line":226},[154,1734,1735],{"class":997},"               check",[154,1737,220],{"class":180},[154,1739,1740],{"class":303},"True",[154,1742,274],{"class":184},[154,1744,1745],{"class":997},"capture_output",[154,1747,220],{"class":180},[154,1749,1740],{"class":303},[154,1751,274],{"class":184},[154,1753,1754],{"class":997},"timeout",[154,1756,220],{"class":180},[154,1758,1759],{"class":303},"120",[154,1761,262],{"class":184},[154,1763,1764,1767,1769,1772,1775,1777,1780,1782,1784,1786,1789],{"class":156,"line":237},[154,1765,1766],{"class":184},"calc ",[154,1768,220],{"class":180},[154,1770,1771],{"class":184}," load_workbook(",[154,1773,1774],{"class":164},"\"calc\u002Flinked.xlsx\"",[154,1776,274],{"class":184},[154,1778,1779],{"class":997},"data_only",[154,1781,220],{"class":180},[154,1783,1740],{"class":303},[154,1785,346],{"class":184},[154,1787,1788],{"class":164},"\"Summary\"",[154,1790,1791],{"class":184},"]\n",[154,1793,1794,1796,1799,1801,1804,1806,1808,1810,1812,1815,1817,1819,1821,1823,1825,1827,1830,1832,1835,1837,1840,1843,1845,1847,1850],{"class":156,"line":248},[154,1795,460],{"class":303},[154,1797,1798],{"class":184},"({",[154,1800,1003],{"class":180},[154,1802,1803],{"class":164},"\"B",[154,1805,567],{"class":566},[154,1807,1175],{"class":184},[154,1809,573],{"class":566},[154,1811,563],{"class":164},[154,1813,1814],{"class":184},": calc[",[154,1816,1003],{"class":180},[154,1818,1803],{"class":164},[154,1820,567],{"class":566},[154,1822,1175],{"class":184},[154,1824,573],{"class":566},[154,1826,563],{"class":164},[154,1828,1829],{"class":184},"].value ",[154,1831,286],{"class":180},[154,1833,1834],{"class":184}," r ",[154,1836,292],{"class":180},[154,1838,1839],{"class":303}," range",[154,1841,1842],{"class":184},"(",[154,1844,1526],{"class":303},[154,1846,274],{"class":184},[154,1848,1849],{"class":303},"7",[154,1851,1852],{"class":184},")})\n",[10,1854,1855,1856,1858,1859,1862,1863,1866,1867,1870,1871,1874,1875,1879],{},"Every cell calculates — including B2, the deliberately unquoted ",[14,1857,824],{}," reference from Step 1. That is a side effect worth understanding: ",[14,1860,1861],{},"rename_sheet"," matched the bare form ",[14,1864,1865],{},"Sales Data!"," and rewrote it as ",[14,1868,1869],{},"'Sales 2026'!",", quotes included, so renaming repaired it. Without the rename, B2 recalculates to an error (",[14,1872,1873],{},"#VALUE!"," in LibreOffice), which is why the check belongs at the end of every job that writes cross-sheet formulas: it is the only place such a mistake becomes visible. ",[27,1876,1878],{"href":1877},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Ffind-formula-errors-in-a-workbook-with-python\u002F","Find #REF!, #DIV\u002F0! and Other Formula Errors in a Workbook"," turns this check into a reusable scan.",[140,1881,1883],{"id":1882},"a-word-on-indirect","A word on INDIRECT",[10,1885,1886,1889,1890,1893],{},[14,1887,1888],{},"=INDIRECT(\"'\" & A1 & \"'!B2\")"," builds a reference from text at calculation time, which is tempting when a summary should follow a sheet name typed in a cell. It works, but it is volatile — it recalculates whenever anything changes — its references are invisible to Excel's trace-precedents tools, and a sheet rename breaks it in a way no helper can fix, because the name lives in a text string. When Python is writing the workbook anyway, write the direct reference instead; reserve ",[14,1891,1892],{},"INDIRECT"," for sheets where users genuinely choose the source at run time.",[140,1895,1897],{"id":1896},"common-pitfalls","Common pitfalls",[1899,1900,1901,1912,1923,1932],"ul",{},[1902,1903,1904,1908,1909,1911],"li",{},[1905,1906,1907],"strong",{},"Unquoted names with spaces."," Always quote; ",[14,1910,534],{}," makes it automatic.",[1902,1913,1914,1917,1918,511,1920,1922],{},[1905,1915,1916],{},"Names that look like addresses."," ",[14,1919,504],{},[14,1921,507],{}," need quotes even though they have no spaces.",[1902,1924,1925,1931],{},[1905,1926,1927,1928,1930],{},"Renaming with ",[14,1929,1097],{}," alone."," Formulas and defined names keep the old name.",[1902,1933,1934,1937],{},[1905,1935,1936],{},"Referencing a sheet before it exists."," openpyxl accepts the formula; the error appears only on calculation. Create sheets first, then write formulas that refer to them.",[140,1939,1941],{"id":1940},"performance-notes","Performance notes",[10,1943,1944],{},"Formula strings cost nothing to write; the rename helper visits every cell once, which takes a second or two on large workbooks and is still far cheaper than debugging a broken summary. If a workbook is regenerated from scratch every run, create sheets with their final names and skip renaming altogether.",[140,1946,1948],{"id":1947},"conclusion","Conclusion",[10,1950,1951,1952,1955,1956,1958],{},"Cross-sheet references are ",[14,1953,1954],{},"sheet!address",", with the sheet name in single quotes whenever it is anything but a plain word — and quoting always is the safe habit. Build references through ",[14,1957,534],{},", prefer defined names for ranges that grow, rename sheets with a helper that rewrites formulas and names because openpyxl will not, and recalculate a copy to confirm every link resolves.",[140,1960,1962],{"id":1961},"frequently-asked-questions","Frequently asked questions",[10,1964,1965,1968],{},[1905,1966,1967],{},"When does a sheet name need quotes in a formula?","\nWhen it contains anything other than letters, digits and underscores, or starts with a digit, or could be read as a cell address — \"Sales Data\", \"O'Brien\", \"2026\", \"Q1\". Quoting always is harmless, so the simplest rule is to always quote.",[10,1970,1971,1974],{},[1905,1972,1973],{},"How do I write a sheet name that contains an apostrophe?","\nDouble the apostrophe inside the quotes: ='O''Brien'!A1. openpyxl.utils.quote_sheetname does this for you.",[10,1976,1977,1980],{},[1905,1978,1979],{},"Does renaming a sheet in openpyxl update formulas?","\nNo. Excel rewrites references when a user renames a sheet, but openpyxl only changes the title. Formulas that used the old name point at a sheet that no longer exists. Update them yourself, as shown on this page.",[10,1982,1983,1986],{},[1905,1984,1985],{},"Should I use INDIRECT to build sheet references from text?","\nOnly when the sheet really must be chosen at calculation time. INDIRECT is volatile, so it recalculates on every change, and its references are invisible to Excel's dependency tools and to renames. Writing the reference directly from Python is almost always better.",[140,1988,1990],{"id":1989},"related","Related",[1899,1992,1993,1999,2006,2013],{},[1902,1994,1995,1996,1998],{},"Up one level: ",[27,1997,30],{"href":29}," — the formula basics.",[1902,2000,2001,2005],{},[27,2002,2004],{"href":2003},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Flink-dashboard-cells-to-data-sheets-with-formulas\u002F","Link Dashboard Cells to Data Sheets with Formulas"," — cross-sheet links in a dashboard.",[1902,2007,2008,2012],{},[27,2009,2011],{"href":2010},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsum-the-same-cell-across-sheets-with-3d-references\u002F","Sum the Same Cell Across Many Sheets with 3D References from Python"," — references spanning a run of sheets.",[1902,2014,2015,2019],{},[27,2016,2018],{"href":2017},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-invalid-worksheet-name-errors\u002F","Fix Invalid Worksheet Name Errors When Writing Excel"," — choosing names that are safe to reference.",[2021,2022,2023],"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 .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}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":150,"searchDepth":194,"depth":194,"links":2025},[2026,2027,2028,2029,2030,2031,2032,2033,2034,2035,2036,2037],{"id":142,"depth":194,"text":143},{"id":379,"depth":194,"text":380},{"id":527,"depth":194,"text":528},{"id":925,"depth":194,"text":926},{"id":1090,"depth":194,"text":1091},{"id":1660,"depth":194,"text":1661},{"id":1882,"depth":194,"text":1883},{"id":1896,"depth":194,"text":1897},{"id":1940,"depth":194,"text":1941},{"id":1947,"depth":194,"text":1948},{"id":1961,"depth":194,"text":1962},{"id":1989,"depth":194,"text":1990},"2026-09-25","Link a summary sheet to its data sheets with formulas like =SUM('Sales Data'!B2:B50). Learn the quoting rules for sheet names with spaces and apostrophes, build references with a helper instead of f-strings, and avoid the openpyxl trap where renaming a sheet silently breaks every formula that pointed at it.","md",[2042,2044,2046,2048],{"q":1967,"a":2043},"When it contains anything other than letters, digits and underscores, or starts with a digit, or could be read as a cell address — \"Sales Data\", \"O'Brien\", \"2026\", \"Q1\". Quoting always is harmless, so the simplest rule is to always quote.",{"q":1973,"a":2045},"Double the apostrophe inside the quotes: ='O''Brien'!A1. openpyxl.utils.quote_sheetname does this for you.",{"q":1979,"a":2047},"No. Excel rewrites references when a user renames a sheet, but openpyxl only changes the title. Formulas that used the old name point at a sheet that no longer exists. Update them yourself, as shown on this page.",{"q":1985,"a":2049},"Only when the sheet really must be chosen at calculation time. INDIRECT is volatile, so it recalculates on every change, and its references are invisible to Excel's dependency tools and to renames. Writing the reference directly from Python is almost always better.","write formulas that reference other sheets with openpyxl",null,{"breadcrumb":2053},[2054,2057,2060],{"name":2055,"item":2056},"Home","\u002F",{"name":2058,"item":2059},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":30,"item":29},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formulas-that-reference-other-sheets-with-openpyxl",{"title":2063,"description":2064},"openpyxl Cross-Sheet Formulas and Quoting Sheet Names","Write formulas that reference other sheets with openpyxl: when to quote sheet names, doubling apostrophes, quote_sheetname, whole-column and defined-name references, INDIRECT, and updating formulas after renaming a sheet.","write-formulas-that-reference-other-sheets-with-openpyxl","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-formulas-that-reference-other-sheets-with-openpyxl\u002Findex",[2068,2069,2070],"excel-formulas","openpyxl","sheets-and-files","how-to","M_Mtf8fulRga5aFzQp5SstCC6IZ4BrTTu0UTZK9-aIk",[2074,2078],{"title":2075,"path":2076,"stem":2077,"children":-1},"Write a Formula to an Excel Cell with openpyxl","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-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",{"title":2079,"path":2080,"stem":2081,"children":-1},"Write Legacy Array Formulas (Ctrl+Shift+Enter) with openpyxl","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-legacy-array-formulas-with-openpyxl","getting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fwrite-legacy-array-formulas-with-openpyxl\u002Findex",[2083,2085],{"path":2084,"title":2058},"\u002Fgetting-started-with-python-excel-automation",{"path":2086,"title":30},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python",[],[2089,2092,2096,2099,2102,2105],{"path":2090,"title":2091,"kind":2071},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fstore-formula-results-with-xlsxwriter","Store Formula Results with xlsxwriter So Every Viewer Shows Values",{"path":2093,"title":2094,"kind":2095},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-cells-with-formula-errors-using-conditional-formatting","Highlight Cells with Formula Errors Using Conditional Formatting","fix",{"path":2097,"title":2098,"kind":2071},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgoal-seek-in-python-instead-of-excel","Goal Seek in Python Instead of Excel",{"path":2100,"title":2101,"kind":2071},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fcheck-that-totals-in-a-report-add-up","Check That Totals in a Report Add Up",{"path":2103,"title":2104,"kind":2071},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcall-excel-worksheet-functions-through-microsoft-graph","Call Excel Worksheet Functions Through Microsoft Graph",{"path":2106,"title":2107,"kind":2071},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frecalculate-excel-online-workbooks-with-graph-sessions","Recalculate Excel Online Workbooks with Graph Sessions",1790972075929]