[{"data":1,"prerenderedAt":1794},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002Fadd-field-to-layer-pyqgis":3},{"id":4,"title":5,"body":6,"description":1783,"extension":1784,"meta":1785,"navigation":260,"path":1790,"seo":1791,"stem":1792,"__hash__":1793},"docs\u002Fspatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002Fadd-field-to-layer-pyqgis\u002Findex.md","Add a Field to a Layer in PyQGIS",{"type":7,"value":8,"toc":1770},"minimark",[9,13,17,26,176,181,207,211,214,324,351,355,358,460,585,611,615,618,942,951,955,962,1068,1193,1225,1229,1232,1334,1337,1484,1496,1500,1506,1571,1578,1582,1646,1650,1659,1663,1676,1692,1701,1716,1733,1739,1743,1766],[10,11,5],"h1",{"id":12},"add-a-field-to-a-layer-in-pyqgis",[14,15,16],"p",{},"Adding a column is the first step of almost every attribute workflow, and it fails in the same two ways for almost everyone: the field is created but the layer insists it does not exist, or the type turns out to be wrong once real values arrive. Both are avoidable with two extra lines — one to refresh the layer's schema cache, and one to check the provider can do what you are asking before you ask.",[14,18,19,20,25],{},"This page is a focused recipe within ",[21,22,24],"a",{"href":23},"\u002Fspatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002F","Attribute Tables and Field Management in PyQGIS",". It covers adding one or several fields, choosing types, making the script idempotent, and the format-specific limits that bite when the target is a shapefile.",[14,27,28],{},[29,30,35,39,43,50,67,76,86,92,97,103,108,111,118,124,128,132,136,140,144,148,152,158,165,167,171],"svg",{"viewBox":31,"role":32,"ariaLabel":33,"xmlns":34},"0 0 760 246","img","The three steps of adding a field: check the provider capability, call addAttributes on the provider, then call updateFields to refresh the layer's cached schema","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[36,37,38],"title",{},"Why updateFields() is not optional",[40,41,42],"desc",{},"A script checks that the provider supports adding attributes, then calls addAttributes, which changes the data source. The layer object still holds a cached field list at that point, so a lookup by name fails. Calling updateFields re-reads the schema into the layer, after which the field resolves correctly.",[44,45],"rect",{"x":46,"y":46,"width":47,"height":48,"fill":49},"0","760","246","#f6f3ea",[51,52,53],"defs",{},[54,55,62],"marker",{"id":56,"viewBox":57,"refX":58,"refY":59,"markerWidth":60,"markerHeight":60,"orient":61},"addFieldArrow","0 0 10 10","8","5","7","auto-start-reverse",[63,64],"path",{"d":65,"fill":66},"M0 0 L10 5 L0 10 z","#2f3b35",[68,69,75],"text",{"x":70,"y":71,"style":72,"fill":73,"textAnchor":74},"380","26","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","The data source changes first; the layer object finds out second",[44,77],{"x":78,"y":79,"width":80,"height":81,"rx":82,"fill":83,"stroke":84,"style":85},"16","86","164","72","10","#fffdf7","#2563eb","stroke-width:2.5",[68,87,91],{"x":88,"y":89,"style":90,"fill":84,"textAnchor":74},"98","114","text-anchor:middle;font-size:12px;font-weight:bold;font-family:sans-serif","capabilities()",[68,93,96],{"x":88,"y":94,"style":95,"fill":66,"textAnchor":74},"136","text-anchor:middle;font-size:11px;font-family:sans-serif","AddAttributes?",[44,98],{"x":99,"y":79,"width":100,"height":81,"rx":82,"fill":101,"stroke":102,"style":85},"220","180","#26322d","#0f766e",[68,104,107],{"x":105,"y":89,"style":90,"fill":106,"textAnchor":74},"310","#d9f99d","addAttributes()",[68,109,110],{"x":105,"y":94,"style":95,"fill":106,"textAnchor":74},"writes to the source",[44,112],{"x":113,"y":114,"width":115,"height":116,"rx":82,"fill":83,"stroke":117,"style":85},"440","42","152","66","#b91c1c",[68,119,123],{"x":120,"y":121,"style":122,"fill":117,"textAnchor":74},"516","68","text-anchor:middle;font-size:11.5px;font-weight:bold;font-family:sans-serif","layer.fields()",[68,125,127],{"x":120,"y":126,"style":95,"fill":66,"textAnchor":74},"88","still the old list",[44,129],{"x":113,"y":130,"width":115,"height":116,"rx":82,"fill":83,"stroke":131,"style":85},"140","#15803d",[68,133,135],{"x":120,"y":134,"style":122,"fill":131,"textAnchor":74},"166","updateFields()",[68,137,139],{"x":120,"y":138,"style":95,"fill":66,"textAnchor":74},"186","re-reads the schema",[44,141],{"x":142,"y":79,"width":143,"height":81,"rx":82,"fill":83,"stroke":131,"style":85},"632","112",[68,145,147],{"x":146,"y":143,"style":122,"fill":131,"textAnchor":74},"688","indexOf()",[68,149,151],{"x":146,"y":150,"style":95,"fill":66,"textAnchor":74},"134","now resolves",[153,154],"line",{"x1":100,"y1":155,"x2":156,"y2":155,"stroke":66,"style":157},"122","216","stroke-width:2.5;marker-end:url(#addFieldArrow)",[153,159],{"x1":160,"y1":161,"x2":162,"y2":163,"stroke":117,"style":164},"400","108","436","80","stroke-width:2;stroke-dasharray:5 4;marker-end:url(#addFieldArrow)",[153,166],{"x1":160,"y1":94,"x2":162,"y2":134,"stroke":131,"style":157},[153,168],{"x1":169,"y1":80,"x2":170,"y2":94,"stroke":131,"style":157},"592","628",[68,172,175],{"x":70,"y":173,"style":95,"fill":174,"textAnchor":74},"228","#59645f","skip updateFields() and indexOf() returns -1 for a field that genuinely exists",[177,178,180],"h2",{"id":179},"prerequisites","Prerequisites",[182,183,184,192,199],"ul",{},[185,186,187,191],"li",{},[188,189,190],"strong",{},"QGIS 3.34 LTR"," (bundled Python 3.12) or newer.",[185,193,194,195,198],{},"A ",[188,196,197],{},"writable"," vector layer. GeoPackage is the recommended target; a WFS or delimited-text layer will reject the operation.",[185,200,201,202,206],{},"Familiarity with the Python Console — see ",[21,203,205],{"href":204},"\u002Fpyqgis-fundamentals-environment-setup\u002Fqgis-python-console-basics\u002F","QGIS Python Console Basics",".",[177,208,210],{"id":209},"add-a-single-field","Add a single field",[14,212,213],{},"Three lines, and the third is the one people forget.",[215,216,221],"pre",{"className":217,"code":218,"language":219,"meta":220,"style":220},"language-python shiki shiki-themes github-dark","from qgis.core import QgsField, QgsProject\nfrom qgis.PyQt.QtCore import QVariant\n\nlayer = QgsProject.instance().mapLayersByName(\"parcels\")[0]\n\nlayer.dataProvider().addAttributes([QgsField(\"value_per_m2\", QVariant.Double)])\nlayer.updateFields()\n\nprint(layer.fields().names())\n","python","",[222,223,224,242,255,262,287,292,304,310,315],"code",{"__ignoreMap":220},[225,226,228,232,236,239],"span",{"class":153,"line":227},1,[225,229,231],{"class":230},"snl16","from",[225,233,235],{"class":234},"s95oV"," qgis.core ",[225,237,238],{"class":230},"import",[225,240,241],{"class":234}," QgsField, QgsProject\n",[225,243,245,247,250,252],{"class":153,"line":244},2,[225,246,231],{"class":230},[225,248,249],{"class":234}," qgis.PyQt.QtCore ",[225,251,238],{"class":230},[225,253,254],{"class":234}," QVariant\n",[225,256,258],{"class":153,"line":257},3,[225,259,261],{"emptyLinePlaceholder":260},true,"\n",[225,263,265,268,271,274,278,281,284],{"class":153,"line":264},4,[225,266,267],{"class":234},"layer ",[225,269,270],{"class":230},"=",[225,272,273],{"class":234}," QgsProject.instance().mapLayersByName(",[225,275,277],{"class":276},"sU2Wk","\"parcels\"",[225,279,280],{"class":234},")[",[225,282,46],{"class":283},"sDLfK",[225,285,286],{"class":234},"]\n",[225,288,290],{"class":153,"line":289},5,[225,291,261],{"emptyLinePlaceholder":260},[225,293,295,298,301],{"class":153,"line":294},6,[225,296,297],{"class":234},"layer.dataProvider().addAttributes([QgsField(",[225,299,300],{"class":276},"\"value_per_m2\"",[225,302,303],{"class":234},", QVariant.Double)])\n",[225,305,307],{"class":153,"line":306},7,[225,308,309],{"class":234},"layer.updateFields()\n",[225,311,313],{"class":153,"line":312},8,[225,314,261],{"emptyLinePlaceholder":260},[225,316,318,321],{"class":153,"line":317},9,[225,319,320],{"class":283},"print",[225,322,323],{"class":234},"(layer.fields().names())\n",[14,325,326,329,330,332,333,337,338,341,342,344,345,347,348,206],{},[188,327,328],{},"Breakdown:"," ",[222,331,107],{}," takes a ",[334,335,336],"em",{},"list",", even for one field, and writes straight to the data source with no undo. ",[222,339,340],{},"QVariant.Double"," is a Qt type rather than a Python one — the mapping is given in the table below. ",[222,343,135],{}," re-reads the schema into the layer object; without it, ",[222,346,123],{}," keeps returning the pre-change list and every lookup by name fails with a confusing ",[222,349,350],{},"-1",[177,352,354],{"id":353},"choose-the-right-type","Choose the right type",[14,356,357],{},"The type is fixed once the field exists, and changing it later means creating a new field and copying values across. It is worth a moment's thought.",[359,360,361,377],"table",{},[362,363,364],"thead",{},[365,366,367,371,374],"tr",{},[368,369,370],"th",{},"Data",[368,372,373],{},"Qt type",[368,375,376],{},"Notes",[378,379,380,398,414,430,443],"tbody",{},[365,381,382,386,391],{},[383,384,385],"td",{},"Whole numbers",[383,387,388],{},[222,389,390],{},"QVariant.Int",[383,392,393,394,397],{},"32-bit; use ",[222,395,396],{},"QVariant.LongLong"," for identifiers above two billion",[365,399,400,403,407],{},[383,401,402],{},"Decimals",[383,404,405],{},[222,406,340],{},[383,408,409,410,413],{},"Set ",[222,411,412],{},"prec"," for the number of decimal places the provider stores",[365,415,416,419,424],{},[383,417,418],{},"Text",[383,420,421],{},[222,422,423],{},"QVariant.String",[383,425,409,426,429],{},[222,427,428],{},"len"," on formats that require it; a shapefile caps names at 10 characters",[365,431,432,435,440],{},[383,433,434],{},"True\u002Ffalse",[383,436,437],{},[222,438,439],{},"QVariant.Bool",[383,441,442],{},"Supported by GeoPackage and PostGIS; a shapefile stores it as an integer",[365,444,445,448,453],{},[383,446,447],{},"A date",[383,449,450],{},[222,451,452],{},"QVariant.Date",[383,454,455,456,459],{},"Not available in shapefile with a time component — use ",[222,457,458],{},"QVariant.DateTime"," on GeoPackage",[215,461,463],{"className":217,"code":462,"language":219,"meta":220,"style":220},"from qgis.core import QgsField\nfrom qgis.PyQt.QtCore import QVariant\n\nfields = [\n    QgsField(\"parcel_ref\", QVariant.String, len=32),\n    QgsField(\"value_per_m2\", QVariant.Double, len=12, prec=2),\n    QgsField(\"surveyed_on\", QVariant.Date),\n    QgsField(\"is_exempt\", QVariant.Bool),\n]\nlayer.dataProvider().addAttributes(fields)\nlayer.updateFields()\n",[222,464,465,476,486,490,500,522,550,560,570,574,580],{"__ignoreMap":220},[225,466,467,469,471,473],{"class":153,"line":227},[225,468,231],{"class":230},[225,470,235],{"class":234},[225,472,238],{"class":230},[225,474,475],{"class":234}," QgsField\n",[225,477,478,480,482,484],{"class":153,"line":244},[225,479,231],{"class":230},[225,481,249],{"class":234},[225,483,238],{"class":230},[225,485,254],{"class":234},[225,487,488],{"class":153,"line":257},[225,489,261],{"emptyLinePlaceholder":260},[225,491,492,495,497],{"class":153,"line":264},[225,493,494],{"class":234},"fields ",[225,496,270],{"class":230},[225,498,499],{"class":234}," [\n",[225,501,502,505,508,511,514,516,519],{"class":153,"line":289},[225,503,504],{"class":234},"    QgsField(",[225,506,507],{"class":276},"\"parcel_ref\"",[225,509,510],{"class":234},", QVariant.String, ",[225,512,428],{"class":513},"s9osk",[225,515,270],{"class":230},[225,517,518],{"class":283},"32",[225,520,521],{"class":234},"),\n",[225,523,524,526,528,531,533,535,538,541,543,545,548],{"class":153,"line":294},[225,525,504],{"class":234},[225,527,300],{"class":276},[225,529,530],{"class":234},", QVariant.Double, ",[225,532,428],{"class":513},[225,534,270],{"class":230},[225,536,537],{"class":283},"12",[225,539,540],{"class":234},", ",[225,542,412],{"class":513},[225,544,270],{"class":230},[225,546,547],{"class":283},"2",[225,549,521],{"class":234},[225,551,552,554,557],{"class":153,"line":306},[225,553,504],{"class":234},[225,555,556],{"class":276},"\"surveyed_on\"",[225,558,559],{"class":234},", QVariant.Date),\n",[225,561,562,564,567],{"class":153,"line":312},[225,563,504],{"class":234},[225,565,566],{"class":276},"\"is_exempt\"",[225,568,569],{"class":234},", QVariant.Bool),\n",[225,571,572],{"class":153,"line":317},[225,573,286],{"class":234},[225,575,577],{"class":153,"line":576},10,[225,578,579],{"class":234},"layer.dataProvider().addAttributes(fields)\n",[225,581,583],{"class":153,"line":582},11,[225,584,309],{"class":234},[14,586,587,589,590,592,593,595,596,598,599,602,603,606,607,610],{},[188,588,328],{}," Adding several fields in one ",[222,591,107],{}," call is a single provider round-trip rather than four, which matters on a network database. ",[222,594,428],{}," and ",[222,597,412],{}," are honoured by providers that store them and ignored by those that do not, so setting them costs nothing and helps where it counts. Note ",[222,600,601],{},"prec=2"," on a ",[222,604,605],{},"Double"," means the ",[334,608,609],{},"stored"," precision — it does not round values you write, it declares how many decimals the column keeps.",[177,612,614],{"id":613},"make-it-safe-to-re-run","Make it safe to re-run",[14,616,617],{},"A script that fails the second time it runs is a script nobody trusts. Guard both the capability and the existence.",[215,619,621],{"className":217,"code":620,"language":219,"meta":220,"style":220},"from qgis.core import QgsField, QgsProject, QgsVectorDataProvider\nfrom qgis.PyQt.QtCore import QVariant\n\nWANTED = {\n    \"value_per_m2\": (QVariant.Double, 12, 2),\n    \"rate_band\": (QVariant.String, 4, 0),\n}\n\nlayer = QgsProject.instance().mapLayersByName(\"parcels\")[0]\nprovider = layer.dataProvider()\n\nif not provider.capabilities() & QgsVectorDataProvider.AddAttributes:\n    raise RuntimeError(f\"{layer.name()} does not support adding fields\")\n\nexisting = set(layer.fields().names())\nto_add = [\n    QgsField(name, qtype, len=length, prec=precision)\n    for name, (qtype, length, precision) in WANTED.items()\n    if name not in existing\n]\n\nif to_add:\n    provider.addAttributes(to_add)\n    layer.updateFields()\n    print(\"added:\", [f.name() for f in to_add])\nelse:\n    print(\"nothing to add\")\n",[222,622,623,634,644,648,659,675,692,697,701,717,727,731,749,782,787,800,810,830,848,866,871,876,884,890,896,921,930],{"__ignoreMap":220},[225,624,625,627,629,631],{"class":153,"line":227},[225,626,231],{"class":230},[225,628,235],{"class":234},[225,630,238],{"class":230},[225,632,633],{"class":234}," QgsField, QgsProject, QgsVectorDataProvider\n",[225,635,636,638,640,642],{"class":153,"line":244},[225,637,231],{"class":230},[225,639,249],{"class":234},[225,641,238],{"class":230},[225,643,254],{"class":234},[225,645,646],{"class":153,"line":257},[225,647,261],{"emptyLinePlaceholder":260},[225,649,650,653,656],{"class":153,"line":264},[225,651,652],{"class":283},"WANTED",[225,654,655],{"class":230}," =",[225,657,658],{"class":234}," {\n",[225,660,661,664,667,669,671,673],{"class":153,"line":289},[225,662,663],{"class":276},"    \"value_per_m2\"",[225,665,666],{"class":234},": (QVariant.Double, ",[225,668,537],{"class":283},[225,670,540],{"class":234},[225,672,547],{"class":283},[225,674,521],{"class":234},[225,676,677,680,683,686,688,690],{"class":153,"line":294},[225,678,679],{"class":276},"    \"rate_band\"",[225,681,682],{"class":234},": (QVariant.String, ",[225,684,685],{"class":283},"4",[225,687,540],{"class":234},[225,689,46],{"class":283},[225,691,521],{"class":234},[225,693,694],{"class":153,"line":306},[225,695,696],{"class":234},"}\n",[225,698,699],{"class":153,"line":312},[225,700,261],{"emptyLinePlaceholder":260},[225,702,703,705,707,709,711,713,715],{"class":153,"line":317},[225,704,267],{"class":234},[225,706,270],{"class":230},[225,708,273],{"class":234},[225,710,277],{"class":276},[225,712,280],{"class":234},[225,714,46],{"class":283},[225,716,286],{"class":234},[225,718,719,722,724],{"class":153,"line":576},[225,720,721],{"class":234},"provider ",[225,723,270],{"class":230},[225,725,726],{"class":234}," layer.dataProvider()\n",[225,728,729],{"class":153,"line":582},[225,730,261],{"emptyLinePlaceholder":260},[225,732,734,737,740,743,746],{"class":153,"line":733},12,[225,735,736],{"class":230},"if",[225,738,739],{"class":230}," not",[225,741,742],{"class":234}," provider.capabilities() ",[225,744,745],{"class":230},"&",[225,747,748],{"class":234}," QgsVectorDataProvider.AddAttributes:\n",[225,750,752,755,758,761,764,767,770,773,776,779],{"class":153,"line":751},13,[225,753,754],{"class":230},"    raise",[225,756,757],{"class":283}," RuntimeError",[225,759,760],{"class":234},"(",[225,762,763],{"class":230},"f",[225,765,766],{"class":276},"\"",[225,768,769],{"class":283},"{",[225,771,772],{"class":234},"layer.name()",[225,774,775],{"class":283},"}",[225,777,778],{"class":276}," does not support adding fields\"",[225,780,781],{"class":234},")\n",[225,783,785],{"class":153,"line":784},14,[225,786,261],{"emptyLinePlaceholder":260},[225,788,790,793,795,798],{"class":153,"line":789},15,[225,791,792],{"class":234},"existing ",[225,794,270],{"class":230},[225,796,797],{"class":283}," set",[225,799,323],{"class":234},[225,801,803,806,808],{"class":153,"line":802},16,[225,804,805],{"class":234},"to_add ",[225,807,270],{"class":230},[225,809,499],{"class":234},[225,811,813,816,818,820,823,825,827],{"class":153,"line":812},17,[225,814,815],{"class":234},"    QgsField(name, qtype, ",[225,817,428],{"class":513},[225,819,270],{"class":230},[225,821,822],{"class":234},"length, ",[225,824,412],{"class":513},[225,826,270],{"class":230},[225,828,829],{"class":234},"precision)\n",[225,831,833,836,839,842,845],{"class":153,"line":832},18,[225,834,835],{"class":230},"    for",[225,837,838],{"class":234}," name, (qtype, length, precision) ",[225,840,841],{"class":230},"in",[225,843,844],{"class":283}," WANTED",[225,846,847],{"class":234},".items()\n",[225,849,851,854,857,860,863],{"class":153,"line":850},19,[225,852,853],{"class":230},"    if",[225,855,856],{"class":234}," name ",[225,858,859],{"class":230},"not",[225,861,862],{"class":230}," in",[225,864,865],{"class":234}," existing\n",[225,867,869],{"class":153,"line":868},20,[225,870,286],{"class":234},[225,872,874],{"class":153,"line":873},21,[225,875,261],{"emptyLinePlaceholder":260},[225,877,879,881],{"class":153,"line":878},22,[225,880,736],{"class":230},[225,882,883],{"class":234}," to_add:\n",[225,885,887],{"class":153,"line":886},23,[225,888,889],{"class":234},"    provider.addAttributes(to_add)\n",[225,891,893],{"class":153,"line":892},24,[225,894,895],{"class":234},"    layer.updateFields()\n",[225,897,899,902,904,907,910,913,916,918],{"class":153,"line":898},25,[225,900,901],{"class":283},"    print",[225,903,760],{"class":234},[225,905,906],{"class":276},"\"added:\"",[225,908,909],{"class":234},", [f.name() ",[225,911,912],{"class":230},"for",[225,914,915],{"class":234}," f ",[225,917,841],{"class":230},[225,919,920],{"class":234}," to_add])\n",[225,922,924,927],{"class":153,"line":923},26,[225,925,926],{"class":230},"else",[225,928,929],{"class":234},":\n",[225,931,933,935,937,940],{"class":153,"line":932},27,[225,934,901],{"class":283},[225,936,760],{"class":234},[225,938,939],{"class":276},"\"nothing to add\"",[225,941,781],{"class":234},[14,943,944,946,947,950],{},[188,945,328],{}," The capability check turns a silent no-op into an explicit failure with the layer's name in it — far easier to diagnose than discovering later that nothing was written. Building ",[222,948,949],{},"to_add"," from a set difference means the script is idempotent: run it ten times and the schema is identical. Declaring the wanted schema as a dictionary at the top also documents it, which matters when the script is the only record of what the table should look like.",[177,952,954],{"id":953},"populate-the-new-field","Populate the new field",[14,956,957,958,961],{},"A field with no values is ",[222,959,960],{},"NULL"," everywhere. Filling it in the same pass keeps the schema and data in step.",[14,963,964],{},[29,965,968,971,974,977,984,987,992,996,1000,1003,1007,1010,1014,1017,1020,1024,1028,1031,1036,1040,1042,1046,1050,1056,1061,1065],{"viewBox":966,"role":32,"ariaLabel":967,"xmlns":34},"0 0 760 236","Two ways to populate a newly added field: a batched provider update built from a Python loop, or the field calculator algorithm which adds and fills in one step",[36,969,970],{},"Two routes from an empty column to a filled one",[40,972,973],{},"An added but empty field can be filled two ways. The Python route iterates the features, builds a dictionary of feature identifier to value, and applies it in a single changeAttributeValues call. The Processing route skips the separate add step entirely, using native:fieldcalculator to create and populate the column in one operation, writing a new layer.",[44,975],{"x":46,"y":46,"width":47,"height":976,"fill":49},"236",[51,978,979],{},[54,980,982],{"id":981,"viewBox":57,"refX":58,"refY":59,"markerWidth":60,"markerHeight":60,"orient":61},"addFillArrow",[63,983],{"d":65,"fill":66},[68,985,986],{"x":70,"y":71,"style":72,"fill":73,"textAnchor":74},"Add then fill, or do both at once",[44,988],{"x":78,"y":989,"width":130,"height":990,"rx":82,"fill":83,"stroke":174,"style":991},"52","60","stroke-width:2",[68,993,995],{"x":79,"y":994,"style":122,"fill":73,"textAnchor":74},"76","field added",[68,997,999],{"x":79,"y":998,"style":95,"fill":66,"textAnchor":74},"96","every value NULL",[44,1001],{"x":156,"y":114,"width":1002,"height":163,"rx":82,"fill":83,"stroke":84,"style":85},"288",[68,1004,1006],{"x":1005,"y":116,"style":122,"fill":84,"textAnchor":74},"360","Python route",[68,1008,1009],{"x":1005,"y":126,"style":95,"fill":66,"textAnchor":74},"loop → {fid: {idx: value}}",[68,1011,1013],{"x":1005,"y":1012,"style":95,"fill":66,"textAnchor":74},"106","one changeAttributeValues call",[44,1015],{"x":156,"y":1016,"width":1002,"height":163,"rx":82,"fill":83,"stroke":131,"style":85},"142",[68,1018,1019],{"x":1005,"y":134,"style":122,"fill":131,"textAnchor":74},"Processing route",[68,1021,1023],{"x":1005,"y":1022,"style":95,"fill":66,"textAnchor":74},"188","native:fieldcalculator",[68,1025,1027],{"x":1005,"y":1026,"style":95,"fill":66,"textAnchor":74},"206","adds and fills in one pass",[44,1029],{"x":1030,"y":114,"width":100,"height":163,"rx":82,"fill":83,"stroke":84,"style":991},"564",[68,1032,1035],{"x":1033,"y":1034,"style":95,"fill":66,"textAnchor":74},"654","70","edits the layer in place",[68,1037,1039],{"x":1033,"y":1038,"style":95,"fill":66,"textAnchor":74},"92","full Python control",[44,1041],{"x":1030,"y":1016,"width":100,"height":163,"rx":82,"fill":83,"stroke":131,"style":991},[68,1043,1045],{"x":1033,"y":1044,"style":95,"fill":66,"textAnchor":74},"170","writes a new layer",[68,1047,1049],{"x":1033,"y":1048,"style":95,"fill":66,"textAnchor":74},"192","safe to re-run",[153,1051],{"x1":1052,"y1":1053,"x2":1054,"y2":1053,"stroke":66,"style":1055},"156","82","212","stroke-width:2;marker-end:url(#addFillArrow)",[153,1057],{"x1":1052,"y1":1058,"x2":1054,"y2":1059,"stroke":131,"style":1060},"94","176","stroke-width:2;stroke-dasharray:5 4;marker-end:url(#addFillArrow)",[153,1062],{"x1":1063,"y1":1053,"x2":1064,"y2":1053,"stroke":84,"style":1055},"504","560",[153,1066],{"x1":1063,"y1":1067,"x2":1064,"y2":1067,"stroke":131,"style":1055},"182",[215,1069,1071],{"className":217,"code":1070,"language":219,"meta":220,"style":220},"index = layer.fields().indexOf(\"value_per_m2\")\nupdates = {}\nfor feature in layer.getFeatures():\n    area = feature.geometry().area()\n    value = feature[\"assessed_value\"]\n    if area and value is not None:\n        updates[feature.id()] = {index: round(value \u002F area, 2)}\n\nlayer.dataProvider().changeAttributeValues(updates)\n",[222,1072,1073,1087,1097,1109,1119,1134,1157,1184,1188],{"__ignoreMap":220},[225,1074,1075,1078,1080,1083,1085],{"class":153,"line":227},[225,1076,1077],{"class":234},"index ",[225,1079,270],{"class":230},[225,1081,1082],{"class":234}," layer.fields().indexOf(",[225,1084,300],{"class":276},[225,1086,781],{"class":234},[225,1088,1089,1092,1094],{"class":153,"line":244},[225,1090,1091],{"class":234},"updates ",[225,1093,270],{"class":230},[225,1095,1096],{"class":234}," {}\n",[225,1098,1099,1101,1104,1106],{"class":153,"line":257},[225,1100,912],{"class":230},[225,1102,1103],{"class":234}," feature ",[225,1105,841],{"class":230},[225,1107,1108],{"class":234}," layer.getFeatures():\n",[225,1110,1111,1114,1116],{"class":153,"line":264},[225,1112,1113],{"class":234},"    area ",[225,1115,270],{"class":230},[225,1117,1118],{"class":234}," feature.geometry().area()\n",[225,1120,1121,1124,1126,1129,1132],{"class":153,"line":289},[225,1122,1123],{"class":234},"    value ",[225,1125,270],{"class":230},[225,1127,1128],{"class":234}," feature[",[225,1130,1131],{"class":276},"\"assessed_value\"",[225,1133,286],{"class":234},[225,1135,1136,1138,1141,1144,1147,1150,1152,1155],{"class":153,"line":294},[225,1137,853],{"class":230},[225,1139,1140],{"class":234}," area ",[225,1142,1143],{"class":230},"and",[225,1145,1146],{"class":234}," value ",[225,1148,1149],{"class":230},"is",[225,1151,739],{"class":230},[225,1153,1154],{"class":283}," None",[225,1156,929],{"class":234},[225,1158,1159,1162,1164,1167,1170,1173,1176,1179,1181],{"class":153,"line":306},[225,1160,1161],{"class":234},"        updates[feature.id()] ",[225,1163,270],{"class":230},[225,1165,1166],{"class":234}," {index: ",[225,1168,1169],{"class":283},"round",[225,1171,1172],{"class":234},"(value ",[225,1174,1175],{"class":230},"\u002F",[225,1177,1178],{"class":234}," area, ",[225,1180,547],{"class":283},[225,1182,1183],{"class":234},")}\n",[225,1185,1186],{"class":153,"line":312},[225,1187,261],{"emptyLinePlaceholder":260},[225,1189,1190],{"class":153,"line":317},[225,1191,1192],{"class":234},"layer.dataProvider().changeAttributeValues(updates)\n",[14,1194,1195,1197,1198,329,1201,1203,1204,1206,1207,595,1209,1212,1213,1216,1217,1219,1220,1224],{},[188,1196,328],{}," Resolving the index ",[334,1199,1200],{},"after",[222,1202,135],{}," is essential — before it, ",[222,1205,147],{}," returns ",[222,1208,350],{},[222,1210,1211],{},"changeAttributeValues()"," silently writes nothing. The dictionary maps feature ID to a dictionary of field index to value, which is the shape the provider expects, and building it first turns ",[334,1214,1215],{},"n"," round-trips into one. The guard skips features with no geometry or a ",[222,1218,960],{}," value rather than writing garbage. See ",[21,1221,1223],{"href":1222},"\u002Fspatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002Fupdate-attribute-values-pyqgis\u002F","Update Attribute Values in Bulk with PyQGIS"," for the transactional alternative.",[177,1226,1228],{"id":1227},"format-limits-worth-knowing","Format limits worth knowing",[14,1230,1231],{},"The provider decides what is possible, and the differences are large enough to change which format you choose. A script that works perfectly against GeoPackage can quietly mangle the same schema written to a shapefile.",[14,1233,1234],{},[29,1235,1238,1241,1244,1247,1250,1254,1260,1264,1268,1273,1326,1330],{"viewBox":1236,"role":32,"ariaLabel":1237,"xmlns":34},"0 0 760 250","A comparison of field support across GeoPackage, PostGIS and shapefile, covering name length, boolean support, date and time support and NULL handling",[36,1239,1240],{},"What each format does to your schema",[40,1242,1243],{},"A three-column comparison. GeoPackage and PostGIS both allow long field names, a true boolean type, full date and time support and reliable NULL storage. Shapefile truncates names to ten characters, stores booleans as integers, has no time component on dates and handles NULL inconsistently.",[44,1245],{"x":46,"y":46,"width":47,"height":1246,"fill":49},"250",[68,1248,1249],{"x":70,"y":71,"style":72,"fill":73,"textAnchor":74},"The format quietly rewrites your schema — pick it deliberately",[153,1251],{"x1":1054,"y1":994,"x2":1252,"y2":994,"stroke":174,"style":1253},"744","stroke-width:1.5",[68,1255,1259],{"x":1256,"y":121,"style":1257,"fill":73,"textAnchor":1258},"200","text-anchor:end;font-size:11.5px;font-weight:bold;font-family:sans-serif","end","capability",[68,1261,1263],{"x":1262,"y":121,"style":122,"fill":131,"textAnchor":74},"300","GeoPackage",[68,1265,1267],{"x":1266,"y":121,"style":122,"fill":131,"textAnchor":74},"470","PostGIS",[68,1269,1272],{"x":1270,"y":121,"style":122,"fill":1271,"textAnchor":74},"650","#b45309","Shapefile",[1274,1275,1277,1281,1285,1288,1291,1294,1297,1299,1302,1306,1309,1311,1314,1318,1321,1323],"g",{"style":1276},"font-size:11.5px;font-family:sans-serif",[68,1278,1280],{"x":1256,"y":1012,"style":1279,"fill":66,"textAnchor":1258},"text-anchor:end","field name length",[68,1282,1284],{"x":1262,"y":1012,"style":1283,"fill":131,"textAnchor":74},"text-anchor:middle","unrestricted",[68,1286,1287],{"x":1266,"y":1012,"style":1283,"fill":131,"textAnchor":74},"63 chars",[68,1289,1290],{"x":1270,"y":1012,"style":1283,"fill":1271,"textAnchor":74},"10 chars — truncated",[68,1292,1293],{"x":1256,"y":130,"style":1279,"fill":66,"textAnchor":1258},"boolean type",[68,1295,1296],{"x":1262,"y":130,"style":1283,"fill":131,"textAnchor":74},"native",[68,1298,1296],{"x":1266,"y":130,"style":1283,"fill":131,"textAnchor":74},[68,1300,1301],{"x":1270,"y":130,"style":1283,"fill":1271,"textAnchor":74},"stored as integer",[68,1303,1305],{"x":1256,"y":1304,"style":1279,"fill":66,"textAnchor":1258},"174","date with time",[68,1307,1308],{"x":1262,"y":1304,"style":1283,"fill":131,"textAnchor":74},"yes",[68,1310,1308],{"x":1266,"y":1304,"style":1283,"fill":131,"textAnchor":74},[68,1312,1313],{"x":1270,"y":1304,"style":1283,"fill":1271,"textAnchor":74},"date only",[68,1315,1317],{"x":1256,"y":1316,"style":1279,"fill":66,"textAnchor":1258},"208","NULL vs empty string",[68,1319,1320],{"x":1262,"y":1316,"style":1283,"fill":131,"textAnchor":74},"distinct",[68,1322,1320],{"x":1266,"y":1316,"style":1283,"fill":131,"textAnchor":74},[68,1324,1325],{"x":1270,"y":1316,"style":1283,"fill":1271,"textAnchor":74},"indistinguishable",[44,1327],{"x":1064,"y":126,"width":100,"height":94,"rx":58,"fill":1271,"fillOpacity":1328,"stroke":1271,"style":1329},0.08,"stroke-width:2;stroke-dasharray:5 4",[68,1331,1333],{"x":70,"y":1332,"style":95,"fill":174,"textAnchor":74},"238","a schema that round-trips through shapefile does not come back the same",[14,1335,1336],{},"If a shapefile is unavoidable, verify the names actually survived rather than assuming they did:",[215,1338,1340],{"className":217,"code":1339,"language":219,"meta":220,"style":220},"WANTED = [\"value_per_square_metre\", \"surveyed_on\"]\n\nlayer.dataProvider().addAttributes([...])\nlayer.updateFields()\n\nactual = layer.fields().names()\nfor name in WANTED:\n    if name not in actual:\n        close = [a for a in actual if a.startswith(name[:8])]\n        print(f\"'{name}' became {close!r}\")\n",[222,1341,1342,1360,1364,1375,1379,1383,1393,1405,1418,1448],{"__ignoreMap":220},[225,1343,1344,1346,1348,1351,1354,1356,1358],{"class":153,"line":227},[225,1345,652],{"class":283},[225,1347,655],{"class":230},[225,1349,1350],{"class":234}," [",[225,1352,1353],{"class":276},"\"value_per_square_metre\"",[225,1355,540],{"class":234},[225,1357,556],{"class":276},[225,1359,286],{"class":234},[225,1361,1362],{"class":153,"line":244},[225,1363,261],{"emptyLinePlaceholder":260},[225,1365,1366,1369,1372],{"class":153,"line":257},[225,1367,1368],{"class":234},"layer.dataProvider().addAttributes([",[225,1370,1371],{"class":283},"...",[225,1373,1374],{"class":234},"])\n",[225,1376,1377],{"class":153,"line":264},[225,1378,309],{"class":234},[225,1380,1381],{"class":153,"line":289},[225,1382,261],{"emptyLinePlaceholder":260},[225,1384,1385,1388,1390],{"class":153,"line":294},[225,1386,1387],{"class":234},"actual ",[225,1389,270],{"class":230},[225,1391,1392],{"class":234}," layer.fields().names()\n",[225,1394,1395,1397,1399,1401,1403],{"class":153,"line":306},[225,1396,912],{"class":230},[225,1398,856],{"class":234},[225,1400,841],{"class":230},[225,1402,844],{"class":283},[225,1404,929],{"class":234},[225,1406,1407,1409,1411,1413,1415],{"class":153,"line":312},[225,1408,853],{"class":230},[225,1410,856],{"class":234},[225,1412,859],{"class":230},[225,1414,862],{"class":230},[225,1416,1417],{"class":234}," actual:\n",[225,1419,1420,1423,1425,1428,1430,1433,1435,1438,1440,1443,1445],{"class":153,"line":317},[225,1421,1422],{"class":234},"        close ",[225,1424,270],{"class":230},[225,1426,1427],{"class":234}," [a ",[225,1429,912],{"class":230},[225,1431,1432],{"class":234}," a ",[225,1434,841],{"class":230},[225,1436,1437],{"class":234}," actual ",[225,1439,736],{"class":230},[225,1441,1442],{"class":234}," a.startswith(name[:",[225,1444,58],{"class":283},[225,1446,1447],{"class":234},"])]\n",[225,1449,1450,1453,1455,1457,1460,1462,1465,1467,1470,1472,1475,1478,1480,1482],{"class":153,"line":576},[225,1451,1452],{"class":283},"        print",[225,1454,760],{"class":234},[225,1456,763],{"class":230},[225,1458,1459],{"class":276},"\"'",[225,1461,769],{"class":283},[225,1463,1464],{"class":234},"name",[225,1466,775],{"class":283},[225,1468,1469],{"class":276},"' became ",[225,1471,769],{"class":283},[225,1473,1474],{"class":234},"close",[225,1476,1477],{"class":230},"!r",[225,1479,775],{"class":283},[225,1481,766],{"class":276},[225,1483,781],{"class":234},[14,1485,1486,1488,1489,1492,1493,1495],{},[188,1487,328],{}," Comparing the requested names against ",[222,1490,1491],{},"layer.fields().names()"," after the fact is the only reliable check, because ",[222,1494,107],{}," reports success even when the driver renames what it stored. Matching on a truncated prefix identifies what the name became, which is what any downstream code will have to reference. The cleaner answer, wherever you have the choice, is to write GeoPackage — the format costs nothing extra and removes all four limitations at once.",[177,1497,1499],{"id":1498},"qgis-version-compatibility","QGIS version compatibility",[14,1501,1502,1503,1505],{},"The examples target ",[188,1504,190],{}," (Python 3.12).",[359,1507,1508,1520],{},[362,1509,1510],{},[365,1511,1512,1515,1518],{},[368,1513,1514],{},"QGIS version",[368,1516,1517],{},"Python",[368,1519,376],{},[378,1521,1522,1537,1548],{},[365,1523,1524,1527,1530],{},[383,1525,1526],{},"3.28 LTR",[383,1528,1529],{},"3.9",[383,1531,1532,1533,1536],{},"Identical API. ",[222,1534,1535],{},"QVariant"," types unchanged.",[365,1538,1539,1542,1545],{},[383,1540,1541],{},"3.34 LTR",[383,1543,1544],{},"3.12",[383,1546,1547],{},"Baseline for this page.",[365,1549,1550,1553,1555],{},[383,1551,1552],{},"3.40 \u002F 3.44",[383,1554,1544],{},[383,1556,1557,1560,1561,1564,1565,1567,1568,1570],{},[222,1558,1559],{},"QgsField"," accepts ",[222,1562,1563],{},"QMetaType"," alongside ",[222,1566,1535],{},"; the ",[222,1569,1535],{}," form still works.",[14,1572,1573,540,1575,1577],{},[222,1574,107],{},[222,1576,135],{}," and the capability flags have been stable across 3.x.",[177,1579,1581],{"id":1580},"troubleshooting","Troubleshooting",[182,1583,1584,1596,1608,1614,1623,1637],{},[185,1585,1586,329,1591,1593,1594,206],{},[188,1587,1588,1590],{},[222,1589,147],{}," returns -1 for a field that exists.",[222,1592,135],{}," was not called after ",[222,1595,107],{},[185,1597,1598,1603,1604,1607],{},[188,1599,1600,1602],{},[222,1601,107],{}," returns False."," The provider does not support it. Check ",[222,1605,1606],{},"capabilities() & QgsVectorDataProvider.AddAttributes"," and confirm the file is not read-only on disk.",[185,1609,1610,1613],{},[188,1611,1612],{},"The field name was truncated."," The target is a shapefile, which caps field names at ten characters. Write to GeoPackage instead.",[185,1615,1616,1619,1620,1622],{},[188,1617,1618],{},"Values come back as text when the field is numeric."," The field was created as ",[222,1621,423],{},". Types cannot be changed in place — add a correctly typed field, copy the values across, and drop the old one.",[185,1624,1625,1628,1629,1632,1633,1636],{},[188,1626,1627],{},"A date field rejects values."," Assign a Python ",[222,1630,1631],{},"datetime.date",", or a ",[222,1634,1635],{},"QDate",", not a string. A string only works if the provider silently coerces it, which not all do.",[185,1638,1639,1642,1643,1645],{},[188,1640,1641],{},"The script fails on its second run."," No existence check. Filter the wanted fields against ",[222,1644,1491],{}," before adding.",[177,1647,1649],{"id":1648},"conclusion","Conclusion",[14,1651,1652,1653,1655,1656,1658],{},"Adding a field is ",[222,1654,107],{}," followed by ",[222,1657,135],{},", with a capability check in front and an existence check to keep the script re-runnable. Spend the extra moment on the type — it is the one decision you cannot revise without rebuilding the column — and prefer GeoPackage over shapefile so names and types survive intact.",[177,1660,1662],{"id":1661},"frequently-asked-questions","Frequently Asked Questions",[14,1664,1665,1668,1669,1672,1673,1675],{},[188,1666,1667],{},"Why does my new field not show up?","\nThe provider has it but the layer object is serving a cached schema. Call ",[222,1670,1671],{},"layer.updateFields()"," straight after ",[222,1674,107],{}," and lookups by name will resolve.",[14,1677,1678,1681,1683,1684,595,1686,1688,1689,1691],{},[188,1679,1680],{},"Which QVariant type should I use for decimals?",[222,1682,340],{},", with ",[222,1685,428],{},[222,1687,412],{}," set if the format stores them. ",[222,1690,412],{}," declares how many decimal places the column keeps; it does not round the values you write.",[14,1693,1694,1697,1698,1700],{},[188,1695,1696],{},"Can I change a field's type after creating it?","\nNot in place. Add a new field with the correct type, copy the values across with ",[222,1699,1211],{},", then delete the original — remembering to delete by descending index if you remove several.",[14,1702,1703,1706,1707,1709,1710,1712,1713,1715],{},[188,1704,1705],{},"Can I add several fields in one call?","\nYes, and you should — ",[222,1708,107],{}," takes a list, so passing four ",[222,1711,1559],{}," objects together is one provider round-trip instead of four. Call ",[222,1714,135],{}," once afterwards.",[14,1717,1718,1721,1722,1725,1726,595,1729,1732],{},[188,1719,1720],{},"Do I need to start an edit session to add a field?","\nNo, if you go through ",[222,1723,1724],{},"layer.dataProvider().addAttributes()",", which writes immediately. Use ",[222,1727,1728],{},"layer.startEditing()",[222,1730,1731],{},"layer.addAttribute()"," only when the change should be undoable, such as inside a plugin.",[14,1734,1735,1738],{},[188,1736,1737],{},"Is there a limit on how many fields a layer can have?","\nFormat-dependent. A shapefile caps out at 255 attribute fields, GeoPackage allows far more, and PostGIS effectively has no practical limit. A table approaching any of those limits is usually better normalised into related tables than widened further.",[177,1740,1742],{"id":1741},"related","Related",[182,1744,1745,1750,1754,1760],{},[185,1746,1747,1749],{},[21,1748,24],{"href":23}," — the guide this recipe belongs to",[185,1751,1752],{},[21,1753,1223],{"href":1222},[185,1755,1756],{},[21,1757,1759],{"href":1758},"\u002Fspatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002Fdelete-fields-pyqgis\u002F","Delete and Rename Fields in PyQGIS",[185,1761,1762],{},[21,1763,1765],{"href":1764},"\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002Fpyqgis-script-to-calculate-polygon-areas\u002F","PyQGIS Script to Calculate Polygon Areas",[1767,1768,1769],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}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 pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":220,"searchDepth":244,"depth":244,"links":1771},[1772,1773,1774,1775,1776,1777,1778,1779,1780,1781,1782],{"id":179,"depth":244,"text":180},{"id":209,"depth":244,"text":210},{"id":353,"depth":244,"text":354},{"id":613,"depth":244,"text":614},{"id":953,"depth":244,"text":954},{"id":1227,"depth":244,"text":1228},{"id":1498,"depth":244,"text":1499},{"id":1580,"depth":244,"text":1581},{"id":1648,"depth":244,"text":1649},{"id":1661,"depth":244,"text":1662},{"id":1741,"depth":244,"text":1742},"Add fields to a vector layer in PyQGIS with QgsField and addAttributes, pick the right type, refresh the layer schema, and keep the script safe to re-run.","md",{"slug":1786,"type":1787,"breadcrumb":1788,"datePublished":1789,"dateModified":1789},"add-field-to-layer-pyqgis","article","Add a Field","2026-08-01","\u002Fspatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002Fadd-field-to-layer-pyqgis",{"title":5,"description":1783},"spatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002Fadd-field-to-layer-pyqgis\u002Findex","faac1bFBvlZKXqldXXrtuBZIzWQIDfFsCOLPnIUVYjY",1785585990475]