[{"data":1,"prerenderedAt":2957},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-a-list-of-dictionaries-to-excel-without-pandas":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-a-list-of-dictionaries-to-excel-without-pandas":2923,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-a-list-of-dictionaries-to-excel-without-pandas":2932,"children:\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-a-list-of-dictionaries-to-excel-without-pandas":2937,"related:\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-a-list-of-dictionaries-to-excel-without-pandas":2938},{"id":4,"title":5,"body":6,"dateModified":2887,"datePublished":2887,"description":2888,"extension":2889,"faq":2890,"keyword":2900,"kind":2901,"meta":2902,"navigation":118,"path":2912,"seo":2913,"slug":2916,"stem":2917,"topics":2918,"type":2921,"__hash__":2922},"docs\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-a-list-of-dictionaries-to-excel-without-pandas\u002Findex.md","Write a List of Dictionaries to Excel Without pandas",{"type":7,"value":8,"toc":2875},"minimark",[9,40,45,70,77,463,616,620,623,758,786,790,793,881,888,892,895,1110,1171,1410,1414,1420,2019,2056,2140,2470,2474,2481,2723,2731,2735,2791,2795,2802,2806,2816,2826,2832,2838,2842,2871],[10,11,12,13,17,18,21,22,25,26,31,32,35,36,39],"p",{},"JSON from an API, rows from a database driver, results built up in a loop — a lot of data in Python arrives as a list of dictionaries. ",[14,15,16],"code",{},"pd.DataFrame(records).to_excel(...)"," is the usual way to save it, but it pulls in pandas for what is really a loop, turns ",[14,19,20],{},"None"," into ",[14,23,24],{},"NaN",", and converts integers in columns with gaps into floats. openpyxl can write the same records directly: one row per dictionary, one column per key, with Python types mapped to Excel types. This page, part of ",[27,28,30],"a",{"href":29},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002F","Writing DataFrames to Excel with pandas",", builds a small ",[14,33,34],{},"DictWriter"," for ",[14,37,38],{},".xlsx"," files and covers the cases that break the naive version.",[41,42,44],"h2",{"id":43},"prerequisites","Prerequisites",[46,47,52],"pre",{"className":48,"code":49,"language":50,"meta":51,"style":51},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[14,53,54],{"__ignoreMap":51},[55,56,59,63,67],"span",{"class":57,"line":58},"line",1,[55,60,62],{"class":61},"sMTad","pip",[55,64,66],{"class":65},"srMev"," install",[55,68,69],{"class":65}," openpyxl\n",[10,71,72,73,76],{},"The records below are what an orders API might return: not every record has every key, one value is a list, one is a nested dictionary, the timestamps carry a timezone and the amounts are ",[14,74,75],{},"Decimal",".",[46,78,82],{"className":79,"code":80,"language":81,"meta":51,"style":51},"language-python shiki shiki-themes github-light github-dark-high-contrast","from datetime import datetime, timezone\nfrom decimal import Decimal\n\nrecords = [\n    {\"id\": 501, \"customer\": \"Arden Ltd\", \"total\": Decimal(\"129.90\"), \"paid\": True,\n     \"created\": datetime(2026, 9, 28, 9, 15, tzinfo=timezone.utc), \"tags\": [\"priority\", \"b2b\"]},\n    {\"id\": 502, \"customer\": \"Birch & Co\", \"total\": Decimal(\"48.00\"), \"paid\": False,\n     \"created\": datetime(2026, 9, 28, 11, 2, tzinfo=timezone.utc)},\n    {\"id\": 503, \"customer\": \"Cobalt GmbH\", \"total\": Decimal(\"310.25\"), \"paid\": True,\n     \"created\": datetime(2026, 9, 29, 8, 40, tzinfo=timezone.utc), \"coupon\": \"AUTUMN10\",\n     \"shipping\": {\"method\": \"express\", \"cost\": Decimal(\"12.50\")}},\n]\nprint(len(records), \"records\")\n","python",[14,83,84,100,113,120,132,184,243,284,320,360,406,436,442],{"__ignoreMap":51},[55,85,86,90,94,97],{"class":57,"line":58},[55,87,89],{"class":88},"s-kum","from",[55,91,93],{"class":92},"skGVy"," datetime ",[55,95,96],{"class":88},"import",[55,98,99],{"class":92}," datetime, timezone\n",[55,101,103,105,108,110],{"class":57,"line":102},2,[55,104,89],{"class":88},[55,106,107],{"class":92}," decimal ",[55,109,96],{"class":88},[55,111,112],{"class":92}," Decimal\n",[55,114,116],{"class":57,"line":115},3,[55,117,119],{"emptyLinePlaceholder":118},true,"\n",[55,121,123,126,129],{"class":57,"line":122},4,[55,124,125],{"class":92},"records ",[55,127,128],{"class":88},"=",[55,130,131],{"class":92}," [\n",[55,133,135,138,141,144,148,151,154,156,159,161,164,167,170,173,176,178,181],{"class":57,"line":134},5,[55,136,137],{"class":92},"    {",[55,139,140],{"class":65},"\"id\"",[55,142,143],{"class":92},": ",[55,145,147],{"class":146},"sP0c6","501",[55,149,150],{"class":92},", ",[55,152,153],{"class":65},"\"customer\"",[55,155,143],{"class":92},[55,157,158],{"class":65},"\"Arden Ltd\"",[55,160,150],{"class":92},[55,162,163],{"class":65},"\"total\"",[55,165,166],{"class":92},": Decimal(",[55,168,169],{"class":65},"\"129.90\"",[55,171,172],{"class":92},"), ",[55,174,175],{"class":65},"\"paid\"",[55,177,143],{"class":92},[55,179,180],{"class":146},"True",[55,182,183],{"class":92},",\n",[55,185,187,190,193,196,198,201,203,206,208,210,212,215,217,221,223,226,229,232,235,237,240],{"class":57,"line":186},6,[55,188,189],{"class":65},"     \"created\"",[55,191,192],{"class":92},": datetime(",[55,194,195],{"class":146},"2026",[55,197,150],{"class":92},[55,199,200],{"class":146},"9",[55,202,150],{"class":92},[55,204,205],{"class":146},"28",[55,207,150],{"class":92},[55,209,200],{"class":146},[55,211,150],{"class":92},[55,213,214],{"class":146},"15",[55,216,150],{"class":92},[55,218,220],{"class":219},"sa561","tzinfo",[55,222,128],{"class":88},[55,224,225],{"class":92},"timezone.utc), ",[55,227,228],{"class":65},"\"tags\"",[55,230,231],{"class":92},": [",[55,233,234],{"class":65},"\"priority\"",[55,236,150],{"class":92},[55,238,239],{"class":65},"\"b2b\"",[55,241,242],{"class":92},"]},\n",[55,244,246,248,250,252,255,257,259,261,264,266,268,270,273,275,277,279,282],{"class":57,"line":245},7,[55,247,137],{"class":92},[55,249,140],{"class":65},[55,251,143],{"class":92},[55,253,254],{"class":146},"502",[55,256,150],{"class":92},[55,258,153],{"class":65},[55,260,143],{"class":92},[55,262,263],{"class":65},"\"Birch & Co\"",[55,265,150],{"class":92},[55,267,163],{"class":65},[55,269,166],{"class":92},[55,271,272],{"class":65},"\"48.00\"",[55,274,172],{"class":92},[55,276,175],{"class":65},[55,278,143],{"class":92},[55,280,281],{"class":146},"False",[55,283,183],{"class":92},[55,285,287,289,291,293,295,297,299,301,303,306,308,311,313,315,317],{"class":57,"line":286},8,[55,288,189],{"class":65},[55,290,192],{"class":92},[55,292,195],{"class":146},[55,294,150],{"class":92},[55,296,200],{"class":146},[55,298,150],{"class":92},[55,300,205],{"class":146},[55,302,150],{"class":92},[55,304,305],{"class":146},"11",[55,307,150],{"class":92},[55,309,310],{"class":146},"2",[55,312,150],{"class":92},[55,314,220],{"class":219},[55,316,128],{"class":88},[55,318,319],{"class":92},"timezone.utc)},\n",[55,321,323,325,327,329,332,334,336,338,341,343,345,347,350,352,354,356,358],{"class":57,"line":322},9,[55,324,137],{"class":92},[55,326,140],{"class":65},[55,328,143],{"class":92},[55,330,331],{"class":146},"503",[55,333,150],{"class":92},[55,335,153],{"class":65},[55,337,143],{"class":92},[55,339,340],{"class":65},"\"Cobalt GmbH\"",[55,342,150],{"class":92},[55,344,163],{"class":65},[55,346,166],{"class":92},[55,348,349],{"class":65},"\"310.25\"",[55,351,172],{"class":92},[55,353,175],{"class":65},[55,355,143],{"class":92},[55,357,180],{"class":146},[55,359,183],{"class":92},[55,361,363,365,367,369,371,373,375,378,380,383,385,388,390,392,394,396,399,401,404],{"class":57,"line":362},10,[55,364,189],{"class":65},[55,366,192],{"class":92},[55,368,195],{"class":146},[55,370,150],{"class":92},[55,372,200],{"class":146},[55,374,150],{"class":92},[55,376,377],{"class":146},"29",[55,379,150],{"class":92},[55,381,382],{"class":146},"8",[55,384,150],{"class":92},[55,386,387],{"class":146},"40",[55,389,150],{"class":92},[55,391,220],{"class":219},[55,393,128],{"class":88},[55,395,225],{"class":92},[55,397,398],{"class":65},"\"coupon\"",[55,400,143],{"class":92},[55,402,403],{"class":65},"\"AUTUMN10\"",[55,405,183],{"class":92},[55,407,409,412,415,418,420,423,425,428,430,433],{"class":57,"line":408},11,[55,410,411],{"class":65},"     \"shipping\"",[55,413,414],{"class":92},": {",[55,416,417],{"class":65},"\"method\"",[55,419,143],{"class":92},[55,421,422],{"class":65},"\"express\"",[55,424,150],{"class":92},[55,426,427],{"class":65},"\"cost\"",[55,429,166],{"class":92},[55,431,432],{"class":65},"\"12.50\"",[55,434,435],{"class":92},")}},\n",[55,437,439],{"class":57,"line":438},12,[55,440,441],{"class":92},"]\n",[55,443,445,448,451,454,457,460],{"class":57,"line":444},13,[55,446,447],{"class":146},"print",[55,449,450],{"class":92},"(",[55,452,453],{"class":146},"len",[55,455,456],{"class":92},"(records), ",[55,458,459],{"class":65},"\"records\"",[55,461,462],{"class":92},")\n",[464,465,473,474,473,478,473,482,473,490,473,500,473,511,473,518,473,523,473,527,473,533,473,538,473,542,473,549,473,555,473,559,473,563,473,566,473,571,473,578,473,583,473,586,473,589,473,593,473,596,473,601,473,608,473,613],"svg",{"viewBox":466,"role":467,"ariaLabelledBy":468,"xmlns":471,"style":472},"4 0 752 142","img",[469,470],"wd-flow-t","wd-flow-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[475,476,477],"title",{"id":469},"From records to a worksheet",[479,480,481],"desc",{"id":470},"The header is collected from every record's keys in first-seen order, each value is converted to an Excel-friendly type, one row is written per record with blanks for missing keys, and the result is formatted as a table.",[483,484],"rect",{"x":485,"y":486,"width":487,"height":488,"fill":489},"4","0","752","142","#ffffff",[483,491],{"x":492,"y":493,"width":494,"height":495,"rx":496,"fill":497,"stroke":498,"style":499},"20.0","16.0","153.0","110.5","12","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[501,502,504],"g",{"transform":503},"translate(84.5,29.0) scale(1.000)",[505,506],"path",{"d":507,"fill":508,"stroke":509,"style":510},"M11 4a7 7 0 1 0 .01 0z M16 16l5 5","none","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[512,513,517],"text",{"x":514,"y":515,"style":516},"96.5","75.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Collect header",[512,519,522],{"x":514,"y":520,"style":521},"95.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","union of keys,",[512,524,526],{"x":514,"y":525,"style":521},"112.5","first-seen order",[57,528],{"x1":529,"y1":530,"x2":531,"y2":530,"stroke":532,"style":499},"178.0","71.2","200.0","var(--brand,#5b5cf0)",[534,535],"polygon",{"points":536,"fill":537},"204.0,71.2 195.0,76.2 195.0,66.2","#5b5cf0",[483,539],{"x":540,"y":493,"width":494,"height":495,"rx":496,"fill":541,"stroke":532,"style":499},"209.0","#f0f4ff",[501,543,545],{"transform":544},"translate(273.5,37.8) scale(1.000)",[505,546],{"d":547,"fill":508,"stroke":548,"style":510},"M12 9a3 3 0 1 0 .01 0z M12 2.5v3 M12 18.5v3 M2.5 12h3 M18.5 12h3 M5.3 5.3l2.1 2.1 M16.6 16.6l2.1 2.1 M5.3 18.7l2.1-2.1 M16.6 7.4l2.1-2.1","var(--brand-strong,#4338ca)",[512,550,554],{"x":551,"y":552,"style":553},"285.5","84.2","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Convert values",[512,556,558],{"x":551,"y":557,"style":521},"103.8","lists to text, tz to naive",[57,560],{"x1":561,"y1":530,"x2":562,"y2":530,"stroke":532,"style":499},"367.0","389.0",[534,564],{"points":565,"fill":537},"393.0,71.2 384.0,76.2 384.0,66.2",[483,567],{"x":568,"y":493,"width":494,"height":495,"rx":496,"fill":569,"stroke":570,"style":499},"398.0","#d9f4f1","var(--teal,#0f9488)",[501,572,574],{"transform":573},"translate(462.5,29.0) scale(1.000)",[505,575],{"d":576,"fill":508,"stroke":577,"style":510},"M4 4h16v16H4z M4 9h16 M4 14h16 M10 4v16","var(--teal-ink,#0b6157)",[512,579,582],{"x":580,"y":515,"style":581},"474.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Write rows",[512,584,585],{"x":580,"y":520,"style":521},"missing key → blank",[512,587,588],{"x":580,"y":525,"style":521},"cell",[57,590],{"x1":591,"y1":530,"x2":592,"y2":530,"stroke":532,"style":499},"556.0","578.0",[534,594],{"points":595,"fill":537},"582.0,71.2 573.0,76.2 573.0,66.2",[483,597],{"x":598,"y":493,"width":494,"height":495,"rx":496,"fill":599,"stroke":600,"style":499},"587.0","#fdefd8","var(--gold,#b4740a)",[501,602,604],{"transform":603},"translate(651.5,37.8) scale(1.000)",[505,605],{"d":606,"fill":508,"stroke":607,"style":510},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14","var(--gold-ink,#7a4e06)",[512,609,612],{"x":610,"y":552,"style":611},"663.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Format",[512,614,615],{"x":610,"y":557,"style":521},"widths, freeze, table",[41,617,619],{"id":618},"step-1-the-naive-version-and-why-it-fails","Step 1 — The naive version and why it fails",[10,621,622],{},"The obvious approach takes the header from the first record and appends each record's values:",[46,624,626],{"className":79,"code":625,"language":81,"meta":51,"style":51},"from openpyxl import Workbook\n\nwb = Workbook()\nws = wb.active\nheader = list(records[0])\nws.append(header)\ntry:\n    for rec in records:\n        ws.append([rec.get(k) for k in header])\nexcept Exception as exc:\n    print(type(exc).__name__, exc)\n",[14,627,628,640,644,654,664,682,687,695,709,725,739],{"__ignoreMap":51},[55,629,630,632,635,637],{"class":57,"line":58},[55,631,89],{"class":88},[55,633,634],{"class":92}," openpyxl ",[55,636,96],{"class":88},[55,638,639],{"class":92}," Workbook\n",[55,641,642],{"class":57,"line":102},[55,643,119],{"emptyLinePlaceholder":118},[55,645,646,649,651],{"class":57,"line":115},[55,647,648],{"class":92},"wb ",[55,650,128],{"class":88},[55,652,653],{"class":92}," Workbook()\n",[55,655,656,659,661],{"class":57,"line":122},[55,657,658],{"class":92},"ws ",[55,660,128],{"class":88},[55,662,663],{"class":92}," wb.active\n",[55,665,666,669,671,674,677,679],{"class":57,"line":134},[55,667,668],{"class":92},"header ",[55,670,128],{"class":88},[55,672,673],{"class":146}," list",[55,675,676],{"class":92},"(records[",[55,678,486],{"class":146},[55,680,681],{"class":92},"])\n",[55,683,684],{"class":57,"line":186},[55,685,686],{"class":92},"ws.append(header)\n",[55,688,689,692],{"class":57,"line":245},[55,690,691],{"class":88},"try",[55,693,694],{"class":92},":\n",[55,696,697,700,703,706],{"class":57,"line":286},[55,698,699],{"class":88},"    for",[55,701,702],{"class":92}," rec ",[55,704,705],{"class":88},"in",[55,707,708],{"class":92}," records:\n",[55,710,711,714,717,720,722],{"class":57,"line":322},[55,712,713],{"class":92},"        ws.append([rec.get(k) ",[55,715,716],{"class":88},"for",[55,718,719],{"class":92}," k ",[55,721,705],{"class":88},[55,723,724],{"class":92}," header])\n",[55,726,727,730,733,736],{"class":57,"line":362},[55,728,729],{"class":88},"except",[55,731,732],{"class":146}," Exception",[55,734,735],{"class":88}," as",[55,737,738],{"class":92}," exc:\n",[55,740,741,744,746,749,752,755],{"class":57,"line":408},[55,742,743],{"class":146},"    print",[55,745,450],{"class":92},[55,747,748],{"class":146},"type",[55,750,751],{"class":92},"(exc).",[55,753,754],{"class":146},"__name__",[55,756,757],{"class":92},", exc)\n",[10,759,760,761,764,765,768,769,772,773,776,777,780,781,785],{},"It fails twice over. openpyxl refuses the list in ",[14,762,763],{},"tags"," with ",[14,766,767],{},"ValueError: Cannot convert ['priority', 'b2b'] to Excel"," — a cell holds one value, not a sequence. And even if it had not, the header came from the first record only, so ",[14,770,771],{},"coupon"," and ",[14,774,775],{},"shipping",", which appear only in the third, would have been dropped without a word. Timezone-aware datetimes are the third trap: Excel has no timezone support, and openpyxl raises a ",[14,778,779],{},"TypeError"," for them as well (see ",[27,782,784],{"href":783},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-excel-does-not-support-timezones-in-datetimes\u002F","Fix \"Excel does not support timezones in datetimes\"",").",[41,787,789],{"id":788},"step-2-build-the-header-from-every-record","Step 2 — Build the header from every record",[10,791,792],{},"Collect keys from all records, keeping the order in which they first appear, so the columns come out in the order a reader expects rather than alphabetically:",[46,794,796],{"className":79,"code":795,"language":81,"meta":51,"style":51},"def collect_header(records):\n    header = {}\n    for rec in records:\n        for key in rec:\n            header.setdefault(key, None)\n    return list(header)\n\nheader = collect_header(records)\nprint(header)\n",[14,797,798,810,820,830,843,852,862,866,875],{"__ignoreMap":51},[55,799,800,803,807],{"class":57,"line":58},[55,801,802],{"class":88},"def",[55,804,806],{"class":805},"s_Opv"," collect_header",[55,808,809],{"class":92},"(records):\n",[55,811,812,815,817],{"class":57,"line":102},[55,813,814],{"class":92},"    header ",[55,816,128],{"class":88},[55,818,819],{"class":92}," {}\n",[55,821,822,824,826,828],{"class":57,"line":115},[55,823,699],{"class":88},[55,825,702],{"class":92},[55,827,705],{"class":88},[55,829,708],{"class":92},[55,831,832,835,838,840],{"class":57,"line":122},[55,833,834],{"class":88},"        for",[55,836,837],{"class":92}," key ",[55,839,705],{"class":88},[55,841,842],{"class":92}," rec:\n",[55,844,845,848,850],{"class":57,"line":134},[55,846,847],{"class":92},"            header.setdefault(key, ",[55,849,20],{"class":146},[55,851,462],{"class":92},[55,853,854,857,859],{"class":57,"line":186},[55,855,856],{"class":88},"    return",[55,858,673],{"class":146},[55,860,861],{"class":92},"(header)\n",[55,863,864],{"class":57,"line":245},[55,865,119],{"emptyLinePlaceholder":118},[55,867,868,870,872],{"class":57,"line":286},[55,869,668],{"class":92},[55,871,128],{"class":88},[55,873,874],{"class":92}," collect_header(records)\n",[55,876,877,879],{"class":57,"line":322},[55,878,447],{"class":146},[55,880,861],{"class":92},[10,882,883,884,887],{},"A dictionary is used as an ordered set: ",[14,885,886],{},"setdefault"," adds a key only the first time it is seen, and dictionaries keep insertion order. If the order matters more than that — say the API returns keys in a different order each time — pass an explicit list of columns and treat any extra key as an error or a warning, so a new field in the API response is noticed rather than appended at the far right.",[41,889,891],{"id":890},"step-3-convert-values-to-types-excel-understands","Step 3 — Convert values to types Excel understands",[10,893,894],{},"Excel cells hold numbers, text, booleans, dates and times. Map everything else before writing:",[46,896,898],{"className":79,"code":897,"language":81,"meta":51,"style":51},"import json\nfrom datetime import date\n\ndef to_cell(value):\n    if isinstance(value, datetime) and value.tzinfo is not None:\n        return value.astimezone(timezone.utc).replace(tzinfo=None)\n    if isinstance(value, (list, tuple, set)):\n        return \", \".join(map(str, value))\n    if isinstance(value, dict):\n        return json.dumps(value, default=str)\n    return value\n\nprint(to_cell(records[0][\"tags\"]), \"|\", to_cell(records[2][\"shipping\"]), \"|\", to_cell(records[0][\"created\"]))\n",[14,899,900,907,918,922,932,960,976,1001,1022,1037,1053,1060,1064],{"__ignoreMap":51},[55,901,902,904],{"class":57,"line":58},[55,903,96],{"class":88},[55,905,906],{"class":92}," json\n",[55,908,909,911,913,915],{"class":57,"line":102},[55,910,89],{"class":88},[55,912,93],{"class":92},[55,914,96],{"class":88},[55,916,917],{"class":92}," date\n",[55,919,920],{"class":57,"line":115},[55,921,119],{"emptyLinePlaceholder":118},[55,923,924,926,929],{"class":57,"line":122},[55,925,802],{"class":88},[55,927,928],{"class":805}," to_cell",[55,930,931],{"class":92},"(value):\n",[55,933,934,937,940,943,946,949,952,955,958],{"class":57,"line":134},[55,935,936],{"class":88},"    if",[55,938,939],{"class":146}," isinstance",[55,941,942],{"class":92},"(value, datetime) ",[55,944,945],{"class":88},"and",[55,947,948],{"class":92}," value.tzinfo ",[55,950,951],{"class":88},"is",[55,953,954],{"class":88}," not",[55,956,957],{"class":146}," None",[55,959,694],{"class":92},[55,961,962,965,968,970,972,974],{"class":57,"line":186},[55,963,964],{"class":88},"        return",[55,966,967],{"class":92}," value.astimezone(timezone.utc).replace(",[55,969,220],{"class":219},[55,971,128],{"class":88},[55,973,20],{"class":146},[55,975,462],{"class":92},[55,977,978,980,982,985,988,990,993,995,998],{"class":57,"line":245},[55,979,936],{"class":88},[55,981,939],{"class":146},[55,983,984],{"class":92},"(value, (",[55,986,987],{"class":146},"list",[55,989,150],{"class":92},[55,991,992],{"class":146},"tuple",[55,994,150],{"class":92},[55,996,997],{"class":146},"set",[55,999,1000],{"class":92},")):\n",[55,1002,1003,1005,1008,1011,1014,1016,1019],{"class":57,"line":286},[55,1004,964],{"class":88},[55,1006,1007],{"class":65}," \", \"",[55,1009,1010],{"class":92},".join(",[55,1012,1013],{"class":146},"map",[55,1015,450],{"class":92},[55,1017,1018],{"class":146},"str",[55,1020,1021],{"class":92},", value))\n",[55,1023,1024,1026,1028,1031,1034],{"class":57,"line":322},[55,1025,936],{"class":88},[55,1027,939],{"class":146},[55,1029,1030],{"class":92},"(value, ",[55,1032,1033],{"class":146},"dict",[55,1035,1036],{"class":92},"):\n",[55,1038,1039,1041,1044,1047,1049,1051],{"class":57,"line":362},[55,1040,964],{"class":88},[55,1042,1043],{"class":92}," json.dumps(value, ",[55,1045,1046],{"class":219},"default",[55,1048,128],{"class":88},[55,1050,1018],{"class":146},[55,1052,462],{"class":92},[55,1054,1055,1057],{"class":57,"line":408},[55,1056,856],{"class":88},[55,1058,1059],{"class":92}," value\n",[55,1061,1062],{"class":57,"line":438},[55,1063,119],{"emptyLinePlaceholder":118},[55,1065,1066,1068,1071,1073,1076,1078,1081,1084,1087,1089,1091,1094,1096,1098,1100,1102,1104,1107],{"class":57,"line":444},[55,1067,447],{"class":146},[55,1069,1070],{"class":92},"(to_cell(records[",[55,1072,486],{"class":146},[55,1074,1075],{"class":92},"][",[55,1077,228],{"class":65},[55,1079,1080],{"class":92},"]), ",[55,1082,1083],{"class":65},"\"|\"",[55,1085,1086],{"class":92},", to_cell(records[",[55,1088,310],{"class":146},[55,1090,1075],{"class":92},[55,1092,1093],{"class":65},"\"shipping\"",[55,1095,1080],{"class":92},[55,1097,1083],{"class":65},[55,1099,1086],{"class":92},[55,1101,486],{"class":146},[55,1103,1075],{"class":92},[55,1105,1106],{"class":65},"\"created\"",[55,1108,1109],{"class":92},"]))\n",[1111,1112,1113,1125,1131,1144],"ul",{},[1114,1115,1116,1120,1121,1124],"li",{},[1117,1118,1119],"strong",{},"Timezones"," are converted to UTC and then dropped. Say so in the header — ",[14,1122,1123],{},"created (UTC)"," — because the cell alone no longer carries it.",[1114,1126,1127,1130],{},[1117,1128,1129],{},"Lists"," become comma-separated text, which reads well and filters in Excel. Use a different separator if the items can contain commas.",[1114,1132,1133,1136,1137,772,1140,1143],{},[1117,1134,1135],{},"Nested dictionaries"," become JSON text. That keeps the data, but a nested object that matters to the reader deserves its own columns: flatten it into ",[14,1138,1139],{},"shipping.method",[14,1141,1142],{},"shipping.cost"," before writing.",[1114,1145,1146,150,1148,150,1151,150,1154,150,1157,1160,1161,1164,1165,1167,1168,1170],{},[1117,1147,75],{},[1117,1149,1150],{},"bool",[1117,1152,1153],{},"int",[1117,1155,1156],{},"float",[1117,1158,1159],{},"date"," and naive ",[1117,1162,1163],{},"datetime"," go through unchanged: openpyxl writes ",[14,1166,75],{}," as a number and ",[14,1169,180],{}," as an Excel boolean.",[464,1172,473,1177,473,1180,473,1183,473,1186,473,1191,473,1196,473,1202,473,1205,473,1209,473,1212,473,1216,473,1220,473,1226,473,1228,473,1235,473,1237,473,1245,473,1247,473,1251,473,1254,473,1257,473,1259,473,1264,473,1266,473,1271,473,1273,473,1277,473,1280,473,1284,473,1286,473,1291,473,1293,473,1298,473,1300,473,1304,473,1307,473,1311,473,1313,473,1318,473,1320,473,1325,473,1327,473,1331,473,1334,473,1338,473,1340,473,1345,473,1347,473,1352,473,1354,473,1358,473,1361,473,1364,473,1366,473,1371,473,1373,473,1378,473,1380,473,1384,473,1387,473,1390,473,1392,473,1397,473,1399,473,1404,473,1406],{"viewBox":1173,"role":467,"ariaLabelledBy":1174,"xmlns":471,"style":472},"4 0 752 310",[1175,1176],"wd-types-t","wd-types-d",[475,1178,1179],{"id":1175},"How each Python type lands in a cell",[479,1181,1182],{"id":1176},"A table of Python types and what happens when openpyxl writes them. int, float, Decimal, bool, date and naive datetime are written natively. Timezone-aware datetime raises an error and must be converted. Lists and dicts raise an error and must be turned into text. None becomes an empty cell.",[483,1184],{"x":485,"y":486,"width":487,"height":1185,"fill":489},"310",[483,1187],{"x":492,"y":493,"width":1188,"height":1189,"rx":486,"fill":489,"stroke":498,"style":1190},"158.9","40.0","stroke-width:1px",[483,1192],{"x":1193,"y":493,"width":1194,"height":1189,"rx":486,"fill":541,"stroke":532,"style":1195},"178.9","187.0","stroke-width:1.2px",[512,1197,1201],{"x":1198,"y":1199,"style":1200},"272.4","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Written as-is",[483,1203],{"x":1204,"y":493,"width":1194,"height":1189,"rx":486,"fill":541,"stroke":532,"style":1195},"365.9",[512,1206,1208],{"x":1207,"y":1199,"style":1200},"459.4","Needs converting",[483,1210],{"x":1211,"y":493,"width":1194,"height":1189,"rx":486,"fill":541,"stroke":532,"style":1195},"553.0",[512,1213,1215],{"x":1214,"y":1199,"style":1200},"646.5","Excel sees",[483,1217],{"x":492,"y":1218,"width":1188,"height":1219,"rx":486,"fill":497,"stroke":498,"style":1190},"56.0","34.0",[512,1221,1225],{"x":1222,"y":1223,"style":1224},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","int, float, Decimal",[483,1227],{"x":1193,"y":1218,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[501,1229,1231],{"transform":1230},"translate(263.4,64.0) scale(0.750)",[505,1232],{"d":1233,"fill":508,"stroke":570,"style":1234},"M5 12.5l4.5 4.5L19 7","stroke-width:2.40px;stroke-linecap:round;stroke-linejoin:round",[483,1236],{"x":1204,"y":1218,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[501,1238,1240],{"transform":1239},"translate(451.4,65.0) scale(0.667)",[505,1241],{"d":1242,"fill":508,"stroke":1243,"style":1244},"M6 6l12 12 M18 6L6 18","var(--accent,#f43f8f)","stroke-width:2.70px;stroke-linecap:round;stroke-linejoin:round",[483,1246],{"x":1211,"y":1218,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[512,1248,1250],{"x":1214,"y":1249,"style":521},"77.0","number",[483,1252],{"x":492,"y":1253,"width":1188,"height":1219,"rx":486,"fill":497,"stroke":498,"style":1190},"90.0",[512,1255,1150],{"x":1222,"y":1256,"style":1224},"112.0",[483,1258],{"x":1193,"y":1253,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[501,1260,1262],{"transform":1261},"translate(263.4,98.0) scale(0.750)",[505,1263],{"d":1233,"fill":508,"stroke":570,"style":1234},[483,1265],{"x":1204,"y":1253,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[501,1267,1269],{"transform":1268},"translate(451.4,99.0) scale(0.667)",[505,1270],{"d":1242,"fill":508,"stroke":1243,"style":1244},[483,1272],{"x":1211,"y":1253,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[512,1274,1276],{"x":1214,"y":1275,"style":521},"111.0","TRUE \u002F FALSE",[483,1278],{"x":492,"y":1279,"width":1188,"height":1219,"rx":486,"fill":497,"stroke":498,"style":1190},"124.0",[512,1281,1283],{"x":1222,"y":1282,"style":1224},"146.0","date, naive datetime",[483,1285],{"x":1193,"y":1279,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[501,1287,1289],{"transform":1288},"translate(263.4,132.0) scale(0.750)",[505,1290],{"d":1233,"fill":508,"stroke":570,"style":1234},[483,1292],{"x":1204,"y":1279,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[501,1294,1296],{"transform":1295},"translate(451.4,133.0) scale(0.667)",[505,1297],{"d":1242,"fill":508,"stroke":1243,"style":1244},[483,1299],{"x":1211,"y":1279,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[512,1301,1303],{"x":1214,"y":1302,"style":521},"145.0","date serial + format",[483,1305],{"x":492,"y":1306,"width":1188,"height":1219,"rx":486,"fill":497,"stroke":498,"style":1190},"158.0",[512,1308,1310],{"x":1222,"y":1309,"style":1224},"180.0","datetime with tzinfo",[483,1312],{"x":1193,"y":1306,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[501,1314,1316],{"transform":1315},"translate(264.4,167.0) scale(0.667)",[505,1317],{"d":1242,"fill":508,"stroke":1243,"style":1244},[483,1319],{"x":1204,"y":1306,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[501,1321,1323],{"transform":1322},"translate(450.4,166.0) scale(0.750)",[505,1324],{"d":1233,"fill":508,"stroke":570,"style":1234},[483,1326],{"x":1211,"y":1306,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[512,1328,1330],{"x":1214,"y":1329,"style":521},"179.0","TypeError until made naive",[483,1332],{"x":492,"y":1333,"width":1188,"height":1219,"rx":486,"fill":497,"stroke":498,"style":1190},"192.0",[512,1335,1337],{"x":1222,"y":1336,"style":1224},"214.0","list, tuple, set",[483,1339],{"x":1193,"y":1333,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[501,1341,1343],{"transform":1342},"translate(264.4,201.0) scale(0.667)",[505,1344],{"d":1242,"fill":508,"stroke":1243,"style":1244},[483,1346],{"x":1204,"y":1333,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[501,1348,1350],{"transform":1349},"translate(450.4,200.0) scale(0.750)",[505,1351],{"d":1233,"fill":508,"stroke":570,"style":1234},[483,1353],{"x":1211,"y":1333,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[512,1355,1357],{"x":1214,"y":1356,"style":521},"213.0","ValueError until joined",[483,1359],{"x":492,"y":1360,"width":1188,"height":1219,"rx":486,"fill":497,"stroke":498,"style":1190},"226.0",[512,1362,1033],{"x":1222,"y":1363,"style":1224},"248.0",[483,1365],{"x":1193,"y":1360,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[501,1367,1369],{"transform":1368},"translate(264.4,235.0) scale(0.667)",[505,1370],{"d":1242,"fill":508,"stroke":1243,"style":1244},[483,1372],{"x":1204,"y":1360,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[501,1374,1376],{"transform":1375},"translate(450.4,234.0) scale(0.750)",[505,1377],{"d":1233,"fill":508,"stroke":570,"style":1234},[483,1379],{"x":1211,"y":1360,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[512,1381,1383],{"x":1214,"y":1382,"style":521},"247.0","JSON text or flatten",[483,1385],{"x":492,"y":1386,"width":1188,"height":1219,"rx":486,"fill":497,"stroke":498,"style":1190},"260.0",[512,1388,20],{"x":1222,"y":1389,"style":1224},"282.0",[483,1391],{"x":1193,"y":1386,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[501,1393,1395],{"transform":1394},"translate(263.4,268.0) scale(0.750)",[505,1396],{"d":1233,"fill":508,"stroke":570,"style":1234},[483,1398],{"x":1204,"y":1386,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[501,1400,1402],{"transform":1401},"translate(451.4,269.0) scale(0.667)",[505,1403],{"d":1242,"fill":508,"stroke":1243,"style":1244},[483,1405],{"x":1211,"y":1386,"width":1194,"height":1219,"rx":486,"fill":489,"stroke":498,"style":1190},[512,1407,1409],{"x":1214,"y":1408,"style":521},"281.0","empty cell",[41,1411,1413],{"id":1412},"step-4-put-it-together-as-a-dictwriter","Step 4 — Put it together as a DictWriter",[10,1415,1416,1417,1419],{},"With the header and the converter, writing is a loop. Flatten nested dictionaries one level first so ",[14,1418,775],{}," becomes two proper columns:",[46,1421,1423],{"className":79,"code":1422,"language":81,"meta":51,"style":51},"from openpyxl.utils import get_column_letter\nfrom openpyxl.worksheet.table import Table, TableStyleInfo\n\ndef flatten(rec, sep=\".\"):\n    out = {}\n    for k, v in rec.items():\n        if isinstance(v, dict):\n            for k2, v2 in v.items():\n                out[f\"{k}{sep}{k2}\"] = v2\n        else:\n            out[k] = v\n    return out\n\ndef write_dicts(path, records, sheet=\"Data\"):\n    flat = [flatten(r) for r in records]\n    header = collect_header(flat)\n    wb = Workbook()\n    ws = wb.active\n    ws.title = sheet\n    ws.append(header)\n    for rec in flat:\n        ws.append([to_cell(rec.get(k)) for k in header])\n    for i, key in enumerate(header, start=1):\n        col = get_column_letter(i)\n        width = max(len(str(key)), *(len(str(to_cell(r.get(key, \"\")))) for r in flat))\n        ws.column_dimensions[col].width = min(width + 2, 40)\n        if key == \"created\":\n            for cell in ws[col][1:]:\n                cell.number_format = \"yyyy-mm-dd hh:mm\"\n    ref = f\"A1:{get_column_letter(len(header))}{len(flat) + 1}\"\n    table = Table(displayName=\"Orders\", ref=ref)\n    table.tableStyleInfo = TableStyleInfo(name=\"TableStyleMedium2\", showRowStripes=True)\n    ws.add_table(table)\n    ws.freeze_panes = \"A2\"\n    wb.save(path)\n    return header\n\ncols = write_dicts(\"orders.xlsx\", records)\nprint(cols)\n",[14,1424,1425,1437,1449,1453,1470,1479,1491,1505,1518,1560,1567,1577,1584,1588,1606,1627,1637,1647,1657,1668,1674,1686,1700,1726,1737,1788,1814,1829,1847,1858,1899,1928,1958,1964,1975,1981,1989,1994,2011],{"__ignoreMap":51},[55,1426,1427,1429,1432,1434],{"class":57,"line":58},[55,1428,89],{"class":88},[55,1430,1431],{"class":92}," openpyxl.utils ",[55,1433,96],{"class":88},[55,1435,1436],{"class":92}," get_column_letter\n",[55,1438,1439,1441,1444,1446],{"class":57,"line":102},[55,1440,89],{"class":88},[55,1442,1443],{"class":92}," openpyxl.worksheet.table ",[55,1445,96],{"class":88},[55,1447,1448],{"class":92}," Table, TableStyleInfo\n",[55,1450,1451],{"class":57,"line":115},[55,1452,119],{"emptyLinePlaceholder":118},[55,1454,1455,1457,1460,1463,1465,1468],{"class":57,"line":122},[55,1456,802],{"class":88},[55,1458,1459],{"class":805}," flatten",[55,1461,1462],{"class":92},"(rec, sep",[55,1464,128],{"class":88},[55,1466,1467],{"class":65},"\".\"",[55,1469,1036],{"class":92},[55,1471,1472,1475,1477],{"class":57,"line":134},[55,1473,1474],{"class":92},"    out ",[55,1476,128],{"class":88},[55,1478,819],{"class":92},[55,1480,1481,1483,1486,1488],{"class":57,"line":186},[55,1482,699],{"class":88},[55,1484,1485],{"class":92}," k, v ",[55,1487,705],{"class":88},[55,1489,1490],{"class":92}," rec.items():\n",[55,1492,1493,1496,1498,1501,1503],{"class":57,"line":245},[55,1494,1495],{"class":88},"        if",[55,1497,939],{"class":146},[55,1499,1500],{"class":92},"(v, ",[55,1502,1033],{"class":146},[55,1504,1036],{"class":92},[55,1506,1507,1510,1513,1515],{"class":57,"line":286},[55,1508,1509],{"class":88},"            for",[55,1511,1512],{"class":92}," k2, v2 ",[55,1514,705],{"class":88},[55,1516,1517],{"class":92}," v.items():\n",[55,1519,1520,1523,1526,1529,1533,1536,1539,1542,1544,1547,1550,1552,1555,1557],{"class":57,"line":322},[55,1521,1522],{"class":92},"                out[",[55,1524,1525],{"class":88},"f",[55,1527,1528],{"class":65},"\"",[55,1530,1532],{"class":1531},"sSjpA","{",[55,1534,1535],{"class":92},"k",[55,1537,1538],{"class":1531},"}{",[55,1540,1541],{"class":92},"sep",[55,1543,1538],{"class":1531},[55,1545,1546],{"class":92},"k2",[55,1548,1549],{"class":1531},"}",[55,1551,1528],{"class":65},[55,1553,1554],{"class":92},"] ",[55,1556,128],{"class":88},[55,1558,1559],{"class":92}," v2\n",[55,1561,1562,1565],{"class":57,"line":362},[55,1563,1564],{"class":88},"        else",[55,1566,694],{"class":92},[55,1568,1569,1572,1574],{"class":57,"line":408},[55,1570,1571],{"class":92},"            out[k] ",[55,1573,128],{"class":88},[55,1575,1576],{"class":92}," v\n",[55,1578,1579,1581],{"class":57,"line":438},[55,1580,856],{"class":88},[55,1582,1583],{"class":92}," out\n",[55,1585,1586],{"class":57,"line":444},[55,1587,119],{"emptyLinePlaceholder":118},[55,1589,1591,1593,1596,1599,1601,1604],{"class":57,"line":1590},14,[55,1592,802],{"class":88},[55,1594,1595],{"class":805}," write_dicts",[55,1597,1598],{"class":92},"(path, records, sheet",[55,1600,128],{"class":88},[55,1602,1603],{"class":65},"\"Data\"",[55,1605,1036],{"class":92},[55,1607,1609,1612,1614,1617,1619,1622,1624],{"class":57,"line":1608},15,[55,1610,1611],{"class":92},"    flat ",[55,1613,128],{"class":88},[55,1615,1616],{"class":92}," [flatten(r) ",[55,1618,716],{"class":88},[55,1620,1621],{"class":92}," r ",[55,1623,705],{"class":88},[55,1625,1626],{"class":92}," records]\n",[55,1628,1630,1632,1634],{"class":57,"line":1629},16,[55,1631,814],{"class":92},[55,1633,128],{"class":88},[55,1635,1636],{"class":92}," collect_header(flat)\n",[55,1638,1640,1643,1645],{"class":57,"line":1639},17,[55,1641,1642],{"class":92},"    wb ",[55,1644,128],{"class":88},[55,1646,653],{"class":92},[55,1648,1650,1653,1655],{"class":57,"line":1649},18,[55,1651,1652],{"class":92},"    ws ",[55,1654,128],{"class":88},[55,1656,663],{"class":92},[55,1658,1660,1663,1665],{"class":57,"line":1659},19,[55,1661,1662],{"class":92},"    ws.title ",[55,1664,128],{"class":88},[55,1666,1667],{"class":92}," sheet\n",[55,1669,1671],{"class":57,"line":1670},20,[55,1672,1673],{"class":92},"    ws.append(header)\n",[55,1675,1677,1679,1681,1683],{"class":57,"line":1676},21,[55,1678,699],{"class":88},[55,1680,702],{"class":92},[55,1682,705],{"class":88},[55,1684,1685],{"class":92}," flat:\n",[55,1687,1689,1692,1694,1696,1698],{"class":57,"line":1688},22,[55,1690,1691],{"class":92},"        ws.append([to_cell(rec.get(k)) ",[55,1693,716],{"class":88},[55,1695,719],{"class":92},[55,1697,705],{"class":88},[55,1699,724],{"class":92},[55,1701,1703,1705,1708,1710,1713,1716,1719,1721,1724],{"class":57,"line":1702},23,[55,1704,699],{"class":88},[55,1706,1707],{"class":92}," i, key ",[55,1709,705],{"class":88},[55,1711,1712],{"class":146}," enumerate",[55,1714,1715],{"class":92},"(header, ",[55,1717,1718],{"class":219},"start",[55,1720,128],{"class":88},[55,1722,1723],{"class":146},"1",[55,1725,1036],{"class":92},[55,1727,1729,1732,1734],{"class":57,"line":1728},24,[55,1730,1731],{"class":92},"        col ",[55,1733,128],{"class":88},[55,1735,1736],{"class":92}," get_column_letter(i)\n",[55,1738,1740,1743,1745,1748,1750,1752,1754,1756,1759,1762,1764,1766,1768,1770,1773,1776,1779,1781,1783,1785],{"class":57,"line":1739},25,[55,1741,1742],{"class":92},"        width ",[55,1744,128],{"class":88},[55,1746,1747],{"class":146}," max",[55,1749,450],{"class":92},[55,1751,453],{"class":146},[55,1753,450],{"class":92},[55,1755,1018],{"class":146},[55,1757,1758],{"class":92},"(key)), ",[55,1760,1761],{"class":88},"*",[55,1763,450],{"class":92},[55,1765,453],{"class":146},[55,1767,450],{"class":92},[55,1769,1018],{"class":146},[55,1771,1772],{"class":92},"(to_cell(r.get(key, ",[55,1774,1775],{"class":65},"\"\"",[55,1777,1778],{"class":92},")))) ",[55,1780,716],{"class":88},[55,1782,1621],{"class":92},[55,1784,705],{"class":88},[55,1786,1787],{"class":92}," flat))\n",[55,1789,1791,1794,1796,1799,1802,1805,1808,1810,1812],{"class":57,"line":1790},26,[55,1792,1793],{"class":92},"        ws.column_dimensions[col].width ",[55,1795,128],{"class":88},[55,1797,1798],{"class":146}," min",[55,1800,1801],{"class":92},"(width ",[55,1803,1804],{"class":88},"+",[55,1806,1807],{"class":146}," 2",[55,1809,150],{"class":92},[55,1811,387],{"class":146},[55,1813,462],{"class":92},[55,1815,1817,1819,1821,1824,1827],{"class":57,"line":1816},27,[55,1818,1495],{"class":88},[55,1820,837],{"class":92},[55,1822,1823],{"class":88},"==",[55,1825,1826],{"class":65}," \"created\"",[55,1828,694],{"class":92},[55,1830,1832,1834,1837,1839,1842,1844],{"class":57,"line":1831},28,[55,1833,1509],{"class":88},[55,1835,1836],{"class":92}," cell ",[55,1838,705],{"class":88},[55,1840,1841],{"class":92}," ws[col][",[55,1843,1723],{"class":146},[55,1845,1846],{"class":92},":]:\n",[55,1848,1850,1853,1855],{"class":57,"line":1849},29,[55,1851,1852],{"class":92},"                cell.number_format ",[55,1854,128],{"class":88},[55,1856,1857],{"class":65}," \"yyyy-mm-dd hh:mm\"\n",[55,1859,1861,1864,1866,1869,1872,1874,1877,1879,1882,1884,1886,1889,1891,1894,1896],{"class":57,"line":1860},30,[55,1862,1863],{"class":92},"    ref ",[55,1865,128],{"class":88},[55,1867,1868],{"class":88}," f",[55,1870,1871],{"class":65},"\"A1:",[55,1873,1532],{"class":1531},[55,1875,1876],{"class":92},"get_column_letter(",[55,1878,453],{"class":146},[55,1880,1881],{"class":92},"(header))",[55,1883,1538],{"class":1531},[55,1885,453],{"class":146},[55,1887,1888],{"class":92},"(flat) ",[55,1890,1804],{"class":88},[55,1892,1893],{"class":146}," 1",[55,1895,1549],{"class":1531},[55,1897,1898],{"class":65},"\"\n",[55,1900,1902,1905,1907,1910,1913,1915,1918,1920,1923,1925],{"class":57,"line":1901},31,[55,1903,1904],{"class":92},"    table ",[55,1906,128],{"class":88},[55,1908,1909],{"class":92}," Table(",[55,1911,1912],{"class":219},"displayName",[55,1914,128],{"class":88},[55,1916,1917],{"class":65},"\"Orders\"",[55,1919,150],{"class":92},[55,1921,1922],{"class":219},"ref",[55,1924,128],{"class":88},[55,1926,1927],{"class":92},"ref)\n",[55,1929,1931,1934,1936,1939,1942,1944,1947,1949,1952,1954,1956],{"class":57,"line":1930},32,[55,1932,1933],{"class":92},"    table.tableStyleInfo ",[55,1935,128],{"class":88},[55,1937,1938],{"class":92}," TableStyleInfo(",[55,1940,1941],{"class":219},"name",[55,1943,128],{"class":88},[55,1945,1946],{"class":65},"\"TableStyleMedium2\"",[55,1948,150],{"class":92},[55,1950,1951],{"class":219},"showRowStripes",[55,1953,128],{"class":88},[55,1955,180],{"class":146},[55,1957,462],{"class":92},[55,1959,1961],{"class":57,"line":1960},33,[55,1962,1963],{"class":92},"    ws.add_table(table)\n",[55,1965,1967,1970,1972],{"class":57,"line":1966},34,[55,1968,1969],{"class":92},"    ws.freeze_panes ",[55,1971,128],{"class":88},[55,1973,1974],{"class":65}," \"A2\"\n",[55,1976,1978],{"class":57,"line":1977},35,[55,1979,1980],{"class":92},"    wb.save(path)\n",[55,1982,1984,1986],{"class":57,"line":1983},36,[55,1985,856],{"class":88},[55,1987,1988],{"class":92}," header\n",[55,1990,1992],{"class":57,"line":1991},37,[55,1993,119],{"emptyLinePlaceholder":118},[55,1995,1997,2000,2002,2005,2008],{"class":57,"line":1996},38,[55,1998,1999],{"class":92},"cols ",[55,2001,128],{"class":88},[55,2003,2004],{"class":92}," write_dicts(",[55,2006,2007],{"class":65},"\"orders.xlsx\"",[55,2009,2010],{"class":92},", records)\n",[55,2012,2014,2016],{"class":57,"line":2013},39,[55,2015,447],{"class":146},[55,2017,2018],{"class":92},"(cols)\n",[10,2020,2021,2022,2024,2025,2028,2029,2031,2032,2035,2036,2038,2039,2042,2043,2047,2048,2051,2052,2055],{},"Missing keys come out as ",[14,2023,20],{}," from ",[14,2026,2027],{},"rec.get(k)",", which openpyxl writes as an empty cell — so ",[14,2030,771],{}," is blank for the first two orders instead of the string ",[14,2033,2034],{},"\"None\""," or a ",[14,2037,24],{},". Column widths are estimated from the longest value, capped at 40 characters, and the ",[14,2040,2041],{},"created"," column gets a date-time format so Excel does not show a bare serial number. The data is wrapped in an ",[27,2044,2046],{"href":2045},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-excel-tables-and-autofilters-with-python\u002Fcreate-an-excel-table-with-python\u002F","Excel table",", which gives it filter buttons and banded rows. Reading the file back shows the types survived — with one caveat: Excel stores every number as a binary float, so ",[14,2049,2050],{},"Decimal(\"129.90\")"," comes back as the float ",[14,2053,2054],{},"129.9",". The two-decimal display is a job for a number format, not for the stored value:",[46,2057,2059],{"className":79,"code":2058,"language":81,"meta":51,"style":51},"from openpyxl import load_workbook\n\nws = load_workbook(\"orders.xlsx\")[\"Data\"]\nfor row in ws.iter_rows(min_row=1, max_row=4, values_only=True):\n    print(row)\n",[14,2060,2061,2072,2076,2094,2133],{"__ignoreMap":51},[55,2062,2063,2065,2067,2069],{"class":57,"line":58},[55,2064,89],{"class":88},[55,2066,634],{"class":92},[55,2068,96],{"class":88},[55,2070,2071],{"class":92}," load_workbook\n",[55,2073,2074],{"class":57,"line":102},[55,2075,119],{"emptyLinePlaceholder":118},[55,2077,2078,2080,2082,2085,2087,2090,2092],{"class":57,"line":115},[55,2079,658],{"class":92},[55,2081,128],{"class":88},[55,2083,2084],{"class":92}," load_workbook(",[55,2086,2007],{"class":65},[55,2088,2089],{"class":92},")[",[55,2091,1603],{"class":65},[55,2093,441],{"class":92},[55,2095,2096,2098,2101,2103,2106,2109,2111,2113,2115,2118,2120,2122,2124,2127,2129,2131],{"class":57,"line":122},[55,2097,716],{"class":88},[55,2099,2100],{"class":92}," row ",[55,2102,705],{"class":88},[55,2104,2105],{"class":92}," ws.iter_rows(",[55,2107,2108],{"class":219},"min_row",[55,2110,128],{"class":88},[55,2112,1723],{"class":146},[55,2114,150],{"class":92},[55,2116,2117],{"class":219},"max_row",[55,2119,128],{"class":88},[55,2121,485],{"class":146},[55,2123,150],{"class":92},[55,2125,2126],{"class":219},"values_only",[55,2128,128],{"class":88},[55,2130,180],{"class":146},[55,2132,1036],{"class":92},[55,2134,2135,2137],{"class":57,"line":134},[55,2136,743],{"class":146},[55,2138,2139],{"class":92},"(row)\n",[464,2141,473,2146,473,2149,473,2152,473,2155,473,2160,473,2165,473,2171,473,2177,473,2181,473,2184,473,2190,473,2194,473,2198,473,2202,473,2206,473,2210,473,2214,473,2218,473,2222,473,2226,473,2230,473,2234,473,2238,473,2242,473,2246,473,2250,473,2253,473,2255,473,2260,473,2264,473,2268,473,2270,473,2274,473,2277,473,2280,473,2282,473,2286,473,2289,473,2292,473,2294,473,2298,473,2301,473,2304,473,2306,473,2309,473,2312,473,2315,473,2317,473,2320,473,2323,473,2326,473,2328,473,2331,473,2334,473,2337,473,2339,473,2342,473,2345,473,2348,473,2351,473,2354,473,2356,473,2359,473,2361,473,2364,473,2366,473,2368,473,2370,473,2373,473,2375,473,2378,473,2380,473,2383,473,2385,473,2387,473,2390,473,2393,473,2395,473,2397,473,2399,473,2402,473,2404,473,2407,473,2409,473,2412,473,2414,473,2417,473,2419,473,2421,473,2423,473,2426,473,2429,473,2431,473,2433,473,2435,473,2438,473,2440,473,2443,473,2445,473,2447,473,2449,473,2452,473,2454,473,2456,473,2459,473,2461,473,2464],{"viewBox":2142,"role":467,"ariaLabelledBy":2143,"xmlns":471,"style":472},"4 0 752 224",[2144,2145],"wd-result-t","wd-result-d",[475,2147,2148],{"id":2144},"orders.xlsx after write_dicts",[479,2150,2151],{"id":2145},"The worksheet written from three records. Columns id, customer, total, paid, created, tags, coupon, shipping.method and shipping.cost, with blank cells where a record had no value.",[483,2153],{"x":485,"y":486,"width":487,"height":2154,"fill":489},"224",[483,2156],{"x":492,"y":493,"width":2157,"height":2158,"rx":382,"fill":497,"stroke":498,"style":2159},"720.0","30.0","stroke-width:1.5px",[483,2161],{"x":492,"y":2162,"width":2157,"height":2163,"rx":486,"fill":497,"stroke":498,"style":2164},"38.0","8.0","stroke-width:0px",[501,2166,2168],{"transform":2167},"translate(30.0,23.0) scale(0.667)",[505,2169],{"d":2170,"fill":508,"stroke":577,"style":1244},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6",[512,2172,2176],{"x":2173,"y":2174,"style":2175},"52.0","36.0","font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:start","orders.xlsx",[483,2178],{"x":492,"y":2179,"width":1222,"height":2180,"rx":486,"fill":497,"stroke":498,"style":1190},"46.0","24.0",[483,2182],{"x":2173,"y":2179,"width":2183,"height":2180,"rx":486,"fill":497,"stroke":498,"style":1190},"51.0",[512,2185,2189],{"x":2186,"y":2187,"style":2188},"77.5","63.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[483,2191],{"x":2192,"y":2179,"width":2193,"height":2180,"rx":486,"fill":497,"stroke":498,"style":1190},"103.0","95.2",[512,2195,2197],{"x":2196,"y":2187,"style":2188},"150.5","B",[483,2199],{"x":2200,"y":2179,"width":2201,"height":2180,"rx":486,"fill":497,"stroke":498,"style":1190},"198.1","64.6",[512,2203,2205],{"x":2204,"y":2187,"style":2188},"230.4","C",[483,2207],{"x":2208,"y":2179,"width":2209,"height":2180,"rx":486,"fill":497,"stroke":498,"style":1190},"262.7","62.8",[512,2211,2213],{"x":2212,"y":2187,"style":2188},"294.1","D",[483,2215],{"x":2216,"y":2179,"width":2217,"height":2180,"rx":486,"fill":497,"stroke":498,"style":1190},"325.5","123.3",[512,2219,2221],{"x":2220,"y":2187,"style":2188},"387.2","E",[483,2223],{"x":2224,"y":2179,"width":2225,"height":2180,"rx":486,"fill":497,"stroke":498,"style":1190},"448.8","90.8",[512,2227,2229],{"x":2228,"y":2187,"style":2188},"494.2","F",[483,2231],{"x":2232,"y":2179,"width":2233,"height":2180,"rx":486,"fill":497,"stroke":498,"style":1190},"539.6","87.4",[512,2235,2237],{"x":2236,"y":2187,"style":2188},"583.3","G",[483,2239],{"x":2240,"y":2179,"width":2241,"height":2180,"rx":486,"fill":497,"stroke":498,"style":1190},"626.9","113.1",[512,2243,2245],{"x":2244,"y":2187,"style":2188},"683.5","H",[483,2247],{"x":492,"y":2248,"width":1222,"height":2249,"rx":486,"fill":497,"stroke":498,"style":1190},"70.0","28.0",[512,2251,1723],{"x":2174,"y":2252,"style":2188},"89.0",[483,2254],{"x":2173,"y":2248,"width":2183,"height":2249,"rx":486,"fill":541,"stroke":532,"style":2159},[512,2256,2259],{"x":2257,"y":2252,"style":2258},"70.5","font-size:11px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","id",[483,2261],{"x":2262,"y":1223,"width":2263,"height":2263,"rx":310,"fill":497,"stroke":498,"style":1190},"85.0","12.0",[534,2265],{"points":2266,"fill":2267},"87.5,82.5 94.5,82.5 91.0,86.5","#5b6780",[483,2269],{"x":2192,"y":2248,"width":2193,"height":2249,"rx":486,"fill":541,"stroke":532,"style":2159},[512,2271,2273],{"x":2272,"y":2252,"style":2258},"143.5","customer",[483,2275],{"x":2276,"y":1223,"width":2263,"height":2263,"rx":310,"fill":497,"stroke":498,"style":1190},"180.1",[534,2278],{"points":2279,"fill":2267},"182.6,82.5 189.6,82.5 186.1,86.5",[483,2281],{"x":2200,"y":2248,"width":2201,"height":2249,"rx":486,"fill":541,"stroke":532,"style":2159},[512,2283,2285],{"x":2284,"y":2252,"style":2258},"223.4","total",[483,2287],{"x":2288,"y":1223,"width":2263,"height":2263,"rx":310,"fill":497,"stroke":498,"style":1190},"244.7",[534,2290],{"points":2291,"fill":2267},"247.2,82.5 254.2,82.5 250.7,86.5",[483,2293],{"x":2208,"y":2248,"width":2209,"height":2249,"rx":486,"fill":541,"stroke":532,"style":2159},[512,2295,2297],{"x":2296,"y":2252,"style":2258},"287.1","paid",[483,2299],{"x":2300,"y":1223,"width":2263,"height":2263,"rx":310,"fill":497,"stroke":498,"style":1190},"307.5",[534,2302],{"points":2303,"fill":2267},"310.0,82.5 317.0,82.5 313.5,86.5",[483,2305],{"x":2216,"y":2248,"width":2217,"height":2249,"rx":486,"fill":541,"stroke":532,"style":2159},[512,2307,2041],{"x":2308,"y":2252,"style":2258},"380.2",[483,2310],{"x":2311,"y":1223,"width":2263,"height":2263,"rx":310,"fill":497,"stroke":498,"style":1190},"430.8",[534,2313],{"points":2314,"fill":2267},"433.3,82.5 440.3,82.5 436.8,86.5",[483,2316],{"x":2224,"y":2248,"width":2225,"height":2249,"rx":486,"fill":541,"stroke":532,"style":2159},[512,2318,763],{"x":2319,"y":2252,"style":2258},"487.2",[483,2321],{"x":2322,"y":1223,"width":2263,"height":2263,"rx":310,"fill":497,"stroke":498,"style":1190},"521.6",[534,2324],{"points":2325,"fill":2267},"524.1,82.5 531.1,82.5 527.6,86.5",[483,2327],{"x":2232,"y":2248,"width":2233,"height":2249,"rx":486,"fill":541,"stroke":532,"style":2159},[512,2329,771],{"x":2330,"y":2252,"style":2258},"576.3",[483,2332],{"x":2333,"y":1223,"width":2263,"height":2263,"rx":310,"fill":497,"stroke":498,"style":1190},"608.9",[534,2335],{"points":2336,"fill":2267},"611.4,82.5 618.4,82.5 614.9,86.5",[483,2338],{"x":2240,"y":2248,"width":2241,"height":2249,"rx":486,"fill":541,"stroke":532,"style":2159},[512,2340,1139],{"x":2341,"y":2252,"style":2258},"676.5",[483,2343],{"x":2344,"y":1223,"width":2263,"height":2263,"rx":310,"fill":497,"stroke":498,"style":1190},"722.0",[534,2346],{"points":2347,"fill":2267},"724.5,82.5 731.5,82.5 728.0,86.5",[483,2349],{"x":492,"y":2350,"width":1222,"height":2249,"rx":486,"fill":497,"stroke":498,"style":1190},"98.0",[512,2352,310],{"x":2174,"y":2353,"style":2188},"117.0",[483,2355],{"x":2173,"y":2350,"width":2183,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2357,147],{"x":2186,"y":2353,"style":2358},"font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[483,2360],{"x":2192,"y":2350,"width":2193,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2362,2363],{"x":2196,"y":2353,"style":2358},"Arden Ltd",[483,2365],{"x":2200,"y":2350,"width":2201,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2367,2054],{"x":2204,"y":2353,"style":2358},[483,2369],{"x":2208,"y":2350,"width":2209,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2371,2372],{"x":2212,"y":2353,"style":2358},"TRUE",[483,2374],{"x":2216,"y":2350,"width":2217,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2376,2377],{"x":2220,"y":2353,"style":2358},"2026-09-28 09:15",[483,2379],{"x":2224,"y":2350,"width":2225,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2381,2382],{"x":2228,"y":2353,"style":2358},"priority, b2b",[483,2384],{"x":2232,"y":2350,"width":2233,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[483,2386],{"x":2240,"y":2350,"width":2241,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[483,2388],{"x":492,"y":2389,"width":1222,"height":2249,"rx":486,"fill":497,"stroke":498,"style":1190},"126.0",[512,2391,2392],{"x":2174,"y":1302,"style":2188},"3",[483,2394],{"x":2173,"y":2389,"width":2183,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2396,254],{"x":2186,"y":1302,"style":2358},[483,2398],{"x":2192,"y":2389,"width":2193,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2400,2401],{"x":2196,"y":1302,"style":2358},"Birch & Co",[483,2403],{"x":2200,"y":2389,"width":2201,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2405,2406],{"x":2204,"y":1302,"style":2358},"48",[483,2408],{"x":2208,"y":2389,"width":2209,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2410,2411],{"x":2212,"y":1302,"style":2358},"FALSE",[483,2413],{"x":2216,"y":2389,"width":2217,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2415,2416],{"x":2220,"y":1302,"style":2358},"2026-09-28 11:02",[483,2418],{"x":2224,"y":2389,"width":2225,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[483,2420],{"x":2232,"y":2389,"width":2233,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[483,2422],{"x":2240,"y":2389,"width":2241,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[483,2424],{"x":492,"y":2425,"width":1222,"height":2249,"rx":486,"fill":497,"stroke":498,"style":1190},"154.0",[512,2427,485],{"x":2174,"y":2428,"style":2188},"173.0",[483,2430],{"x":2173,"y":2425,"width":2183,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2432,331],{"x":2186,"y":2428,"style":2358},[483,2434],{"x":2192,"y":2425,"width":2193,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2436,2437],{"x":2196,"y":2428,"style":2358},"Cobalt GmbH",[483,2439],{"x":2200,"y":2425,"width":2201,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2441,2442],{"x":2204,"y":2428,"style":2358},"310.25",[483,2444],{"x":2208,"y":2425,"width":2209,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2446,2372],{"x":2212,"y":2428,"style":2358},[483,2448],{"x":2216,"y":2425,"width":2217,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2450,2451],{"x":2220,"y":2428,"style":2358},"2026-09-29 08:40",[483,2453],{"x":2224,"y":2425,"width":2225,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[483,2455],{"x":2232,"y":2425,"width":2233,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2457,2458],{"x":2236,"y":2428,"style":2358},"AUTUMN10",[483,2460],{"x":2240,"y":2425,"width":2241,"height":2249,"rx":486,"fill":489,"stroke":498,"style":1190},[512,2462,2463],{"x":2244,"y":2428,"style":2358},"express",[512,2465,2469],{"x":2466,"y":2467,"style":2468},"380.0","204.0","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","The shipping.cost column to the right is cut off here; blanks are real empty cells.",[41,2471,2473],{"id":2472},"step-5-large-lists-write-only-mode","Step 5 — Large lists: write-only mode",[10,2475,2476,2477,2480],{},"For hundreds of thousands of records, a normal workbook keeps every cell object in memory until ",[14,2478,2479],{},"save",". Write-only mode streams rows to disk as they are appended. It cannot go back to earlier rows, so the header must be known before the first row — which means either a fixed column list or one pass over the records to collect keys:",[46,2482,2484],{"className":79,"code":2483,"language":81,"meta":51,"style":51},"import time\n\nbig = [{\"id\": i, \"sku\": f\"SKU-{i % 500:04d}\", \"qty\": i % 7, \"price\": Decimal(\"9.99\")} for i in range(200_000)]\nt = time.perf_counter()\nwb = Workbook(write_only=True)\nws = wb.create_sheet(\"Data\")\nheader = collect_header(big[:1000])\nws.append(header)\nfor rec in big:\n    ws.append([rec.get(k) for k in header])\nwb.save(\"big.xlsx\")\nprint(f\"{len(big):,} rows in {time.perf_counter() - t:.1f}s\")\n",[14,2485,2486,2493,2497,2584,2594,2612,2625,2639,2643,2654,2667,2677],{"__ignoreMap":51},[55,2487,2488,2490],{"class":57,"line":58},[55,2489,96],{"class":88},[55,2491,2492],{"class":92}," time\n",[55,2494,2495],{"class":57,"line":102},[55,2496,119],{"emptyLinePlaceholder":118},[55,2498,2499,2502,2504,2507,2509,2512,2515,2517,2519,2522,2524,2527,2530,2533,2536,2538,2540,2542,2545,2548,2550,2553,2555,2558,2560,2563,2566,2568,2571,2573,2576,2578,2581],{"class":57,"line":115},[55,2500,2501],{"class":92},"big ",[55,2503,128],{"class":88},[55,2505,2506],{"class":92}," [{",[55,2508,140],{"class":65},[55,2510,2511],{"class":92},": i, ",[55,2513,2514],{"class":65},"\"sku\"",[55,2516,143],{"class":92},[55,2518,1525],{"class":88},[55,2520,2521],{"class":65},"\"SKU-",[55,2523,1532],{"class":1531},[55,2525,2526],{"class":92},"i ",[55,2528,2529],{"class":88},"%",[55,2531,2532],{"class":146}," 500",[55,2534,2535],{"class":88},":04d",[55,2537,1549],{"class":1531},[55,2539,1528],{"class":65},[55,2541,150],{"class":92},[55,2543,2544],{"class":65},"\"qty\"",[55,2546,2547],{"class":92},": i ",[55,2549,2529],{"class":88},[55,2551,2552],{"class":146}," 7",[55,2554,150],{"class":92},[55,2556,2557],{"class":65},"\"price\"",[55,2559,166],{"class":92},[55,2561,2562],{"class":65},"\"9.99\"",[55,2564,2565],{"class":92},")} ",[55,2567,716],{"class":88},[55,2569,2570],{"class":92}," i ",[55,2572,705],{"class":88},[55,2574,2575],{"class":146}," range",[55,2577,450],{"class":92},[55,2579,2580],{"class":146},"200_000",[55,2582,2583],{"class":92},")]\n",[55,2585,2586,2589,2591],{"class":57,"line":122},[55,2587,2588],{"class":92},"t ",[55,2590,128],{"class":88},[55,2592,2593],{"class":92}," time.perf_counter()\n",[55,2595,2596,2598,2600,2603,2606,2608,2610],{"class":57,"line":134},[55,2597,648],{"class":92},[55,2599,128],{"class":88},[55,2601,2602],{"class":92}," Workbook(",[55,2604,2605],{"class":219},"write_only",[55,2607,128],{"class":88},[55,2609,180],{"class":146},[55,2611,462],{"class":92},[55,2613,2614,2616,2618,2621,2623],{"class":57,"line":186},[55,2615,658],{"class":92},[55,2617,128],{"class":88},[55,2619,2620],{"class":92}," wb.create_sheet(",[55,2622,1603],{"class":65},[55,2624,462],{"class":92},[55,2626,2627,2629,2631,2634,2637],{"class":57,"line":245},[55,2628,668],{"class":92},[55,2630,128],{"class":88},[55,2632,2633],{"class":92}," collect_header(big[:",[55,2635,2636],{"class":146},"1000",[55,2638,681],{"class":92},[55,2640,2641],{"class":57,"line":286},[55,2642,686],{"class":92},[55,2644,2645,2647,2649,2651],{"class":57,"line":322},[55,2646,716],{"class":88},[55,2648,702],{"class":92},[55,2650,705],{"class":88},[55,2652,2653],{"class":92}," big:\n",[55,2655,2656,2659,2661,2663,2665],{"class":57,"line":362},[55,2657,2658],{"class":92},"    ws.append([rec.get(k) ",[55,2660,716],{"class":88},[55,2662,719],{"class":92},[55,2664,705],{"class":88},[55,2666,724],{"class":92},[55,2668,2669,2672,2675],{"class":57,"line":408},[55,2670,2671],{"class":92},"wb.save(",[55,2673,2674],{"class":65},"\"big.xlsx\"",[55,2676,462],{"class":92},[55,2678,2679,2681,2683,2685,2687,2689,2691,2694,2697,2699,2702,2704,2707,2710,2713,2716,2718,2721],{"class":57,"line":438},[55,2680,447],{"class":146},[55,2682,450],{"class":92},[55,2684,1525],{"class":88},[55,2686,1528],{"class":65},[55,2688,1532],{"class":1531},[55,2690,453],{"class":146},[55,2692,2693],{"class":92},"(big)",[55,2695,2696],{"class":88},":,",[55,2698,1549],{"class":1531},[55,2700,2701],{"class":65}," rows in ",[55,2703,1532],{"class":1531},[55,2705,2706],{"class":92},"time.perf_counter() ",[55,2708,2709],{"class":88},"-",[55,2711,2712],{"class":92}," t",[55,2714,2715],{"class":88},":.1f",[55,2717,1549],{"class":1531},[55,2719,2720],{"class":65},"s\"",[55,2722,462],{"class":92},[10,2724,2725,2726,2730],{},"Collecting the header from a sample is safe only when every record has the same keys, as rows from a database query do. For API data with optional fields, do a full first pass — it is cheap compared with writing. ",[27,2727,2729],{"href":2728},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fwrite-large-dataframes-to-excel-with-write-only-mode\u002F","Write Large DataFrames to Excel with Write-Only Mode"," covers styling rows in this mode.",[41,2732,2734],{"id":2733},"common-pitfalls","Common pitfalls",[1111,2736,2737,2743,2753,2762,2776],{},[1114,2738,2739,2742],{},[1117,2740,2741],{},"Header from the first record only."," Optional keys disappear. Collect from all records, or fix the column list.",[1114,2744,2745,2752],{},[1117,2746,2747,2748,2751],{},"Writing ",[14,2749,2750],{},"str(value)"," for everything."," Numbers stored as text trigger Excel's green-triangle warnings and break sums. Only convert what Excel cannot store.",[1114,2754,2755,2758,2759,2761],{},[1117,2756,2757],{},"Timezone-aware datetimes."," Convert to one zone and strip ",[14,2760,220],{},", and name the zone in the header.",[1114,2763,2764,2767,2768,2771,2772,76],{},[1117,2765,2766],{},"Illegal characters."," Control characters in text from APIs raise ",[14,2769,2770],{},"IllegalCharacterError","; see ",[27,2773,2775],{"href":2774},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-illegalcharactererror-when-writing-excel-with-openpyxl\u002F","Fix IllegalCharacterError When Writing Excel with openpyxl",[1114,2777,2778,2783,2784,2786,2787,76],{},[1117,2779,2780,2781,76],{},"Text that starts with ",[14,2782,128],{}," openpyxl writes a string beginning with ",[14,2785,128],{}," as a formula. For data from outside, that is a security issue, covered in ",[27,2788,2790],{"href":2789},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation\u002Fprevent-formula-injection-in-excel-exports\u002F","Prevent Formula Injection in Excel Exports",[41,2792,2794],{"id":2793},"conclusion","Conclusion",[10,2796,2797,2798,2801],{},"Collect the header from every record in first-seen order, convert the few types Excel cannot store — timezone-aware datetimes, lists, nested dictionaries — and append one row per record with ",[14,2799,2800],{},"rec.get(key)"," so missing keys become blank cells. Flatten one level of nesting into dotted column names, set widths and a date format, and wrap the range in a table. For very long lists, fix the header first and write in write-only mode.",[41,2803,2805],{"id":2804},"frequently-asked-questions","Frequently asked questions",[10,2807,2808,2811,2812,2815],{},[1117,2809,2810],{},"How do I write a list of dictionaries to Excel without pandas?","\nCreate an openpyxl Workbook, append a header row built from the dictionaries' keys, then append one row per dictionary with ",[55,2813,2814],{},"rec.get(k) for k in header",". Missing keys become empty cells.",[10,2817,2818,2825],{},[1117,2819,2820,2821,2824],{},"Why does openpyxl say \"Cannot convert ",[55,2822,2823],{},"..."," to Excel\"?","\nA cell can hold only a single number, string, boolean or date. Convert lists and dictionaries to text — a joined string or JSON — or split them into separate columns before writing.",[10,2827,2828,2831],{},[1117,2829,2830],{},"How do I keep the column order of my dictionaries?","\nBuild the header by walking the records and adding each key the first time it appears. Python dictionaries keep insertion order, so the columns follow the order of the keys in the data.",[10,2833,2834,2837],{},[1117,2835,2836],{},"Is openpyxl slower than pandas for this?","\nFor writing, pandas uses openpyxl or xlsxwriter underneath, so the speed is similar; pandas mostly adds conversion overhead. For very large lists use openpyxl's write-only mode, or xlsxwriter's constant_memory mode.",[41,2839,2841],{"id":2840},"related","Related",[1111,2843,2844,2850,2857,2864],{},[1114,2845,2846,2847,2849],{},"Up one level: ",[27,2848,30],{"href":29}," — the pandas route and its options.",[1114,2851,2852,2856],{},[27,2853,2855],{"href":2854},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fread-excel-rows-into-dictionaries-with-openpyxl\u002F","Read Excel Rows into Dictionaries with openpyxl"," — the reverse direction.",[1114,2858,2859,2863],{},[27,2860,2862],{"href":2861},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fhow-openpyxl-stores-python-values-in-cells\u002F","How openpyxl Stores Python Values in Cells"," — the type mapping in detail.",[1114,2865,2866,2870],{},[27,2867,2869],{"href":2868},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmoving-data-between-excel-and-databases\u002Ffetch-api-data-into-excel-with-python-requests\u002F","Fetch API Data into Excel with Python requests"," — where records like these come from.",[2872,2873,2874],"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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}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}",{"title":51,"searchDepth":102,"depth":102,"links":2876},[2877,2878,2879,2880,2881,2882,2883,2884,2885,2886],{"id":43,"depth":102,"text":44},{"id":618,"depth":102,"text":619},{"id":788,"depth":102,"text":789},{"id":890,"depth":102,"text":891},{"id":1412,"depth":102,"text":1413},{"id":2472,"depth":102,"text":2473},{"id":2733,"depth":102,"text":2734},{"id":2793,"depth":102,"text":2794},{"id":2804,"depth":102,"text":2805},{"id":2840,"depth":102,"text":2841},"2026-09-29","Write records from an API, a database cursor or your own code straight to .xlsx with openpyxl: a header from the union of keys, missing keys left blank, nested values flattened, dates and decimals kept as real Excel types, write-only mode for large lists, and a formatted table at the end.","md",[2891,2893,2896,2898],{"q":2810,"a":2892},"Create an openpyxl Workbook, append a header row built from the dictionaries' keys, then append one row per dictionary with [rec.get(k) for k in header]. Missing keys become empty cells.",{"q":2894,"a":2895},"Why does openpyxl say \"Cannot convert [...] to Excel\"?","A cell can hold only a single number, string, boolean or date. Convert lists and dictionaries to text — a joined string or JSON — or split them into separate columns before writing.",{"q":2830,"a":2897},"Build the header by walking the records and adding each key the first time it appears. Python dictionaries keep insertion order, so the columns follow the order of the keys in the data.",{"q":2836,"a":2899},"For writing, pandas uses openpyxl or xlsxwriter underneath, so the speed is similar; pandas mostly adds conversion overhead. For very large lists use openpyxl's write-only mode, or xlsxwriter's constant_memory mode.","write a list of dictionaries to excel without pandas",null,{"breadcrumb":2903},[2904,2907,2910],{"name":2905,"item":2906},"Home","\u002F",{"name":2908,"item":2909},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":2911,"item":29},"Writing DataFrames to Excel with Pandas","\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-a-list-of-dictionaries-to-excel-without-pandas",{"title":2914,"description":2915},"Write a List of Dicts to Excel in Python Without pandas","A DictWriter for Excel with openpyxl: collect headers from every record, fill missing keys, convert lists and dicts, handle datetimes with timezones and Decimal values, stream large lists in write-only mode and add an Excel table.","write-a-list-of-dictionaries-to-excel-without-pandas","getting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-a-list-of-dictionaries-to-excel-without-pandas\u002Findex",[2919,2920],"writing-excel","openpyxl","how-to","RCntWyBjeg44ASc-vCYrNLk_8JQgBO975qV5j116ACo",[2924,2928],{"title":2925,"path":2926,"stem":2927,"children":-1},"Write a DataFrame to a Specific Cell in Excel with pandas","\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-a-dataframe-to-a-specific-cell-in-excel","getting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-a-dataframe-to-a-specific-cell-in-excel\u002Findex",{"title":2929,"path":2930,"stem":2931,"children":-1},"Write a MultiIndex DataFrame to Excel with pandas","\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-a-multiindex-dataframe-to-excel","getting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fwrite-a-multiindex-dataframe-to-excel\u002Findex",[2933,2935],{"path":2934,"title":2908},"\u002Fgetting-started-with-python-excel-automation",{"path":2936,"title":2911},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas",[],[2939,2942,2945,2948,2951,2954],{"path":2940,"title":2941,"kind":2921},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fwrite-multiple-dataframes-to-one-excel-file","Write Multiple DataFrames to One Excel File",{"path":2943,"title":2944,"kind":2921},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-formatted-excel-report-with-xlsxwriter","Write a Formatted Excel Report with xlsxwriter",{"path":2946,"title":2947,"kind":2921},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-and-replace-text-in-excel-with-openpyxl","Find and Replace Text in Excel with openpyxl",{"path":2949,"title":2950,"kind":2921},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Finsert-and-delete-rows-and-columns-with-openpyxl","Insert and Delete Rows and Columns with openpyxl",{"path":2952,"title":2953,"kind":2921},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fopenpyxl-append-data-to-existing-excel-sheet","openpyxl: Append Data to an Existing Excel Sheet",{"path":2955,"title":2956,"kind":2921},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Freorder-columns-to-match-a-template-layout-with-pandas","Reorder Columns to Match a Template Layout with pandas",1790972077765]