[{"data":1,"prerenderedAt":1547},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fpostgis-and-database-workflows\u002Fwrite-vector-layer-to-geopackage-pyqgis":3},{"id":4,"title":5,"body":6,"description":1536,"extension":1537,"meta":1538,"navigation":230,"path":1543,"seo":1544,"stem":1545,"__hash__":1546},"docs\u002Fspatial-data-processing-automation\u002Fpostgis-and-database-workflows\u002Fwrite-vector-layer-to-geopackage-pyqgis\u002Findex.md","Write a Vector Layer to GeoPackage in PyQGIS",{"type":7,"value":8,"toc":1523},"minimark",[9,13,17,26,138,143,169,173,422,439,443,450,490,510,650,654,657,768,789,793,796,903,923,927,930,1047,1059,1062,1154,1167,1267,1271,1277,1349,1356,1360,1414,1418,1427,1431,1437,1446,1463,1475,1488,1492,1519],[10,11,5],"h1",{"id":12},"write-a-vector-layer-to-geopackage-in-pyqgis",[14,15,16],"p",{},"Every analysis eventually has to land somewhere. GeoPackage is the right default: one file, many layers, real field types, no ten-character column limit, no encoding guesswork, and a spatial index that makes it fast to read back. The only thing that catches people out is the option that decides whether you are adding a layer to the file or replacing the entire file — and getting that wrong in a scheduled job deletes work.",[14,18,19,20,25],{},"This recipe belongs to ",[21,22,24],"a",{"href":23},"\u002Fspatial-data-processing-automation\u002Fpostgis-and-database-workflows\u002F","PostGIS and Database Workflows in PyQGIS",". It covers the modern writer API, appending layers safely, renaming and dropping fields on the way out, and saving the layer's style inside the container so the next person opens a finished map rather than grey polygons.",[14,27,28],{},[29,30,35,39,43,50,59,69,75,85,91,94,98,101,105,109,114,118,121,124,126,129,132,135],"svg",{"viewBox":31,"role":32,"ariaLabel":33,"xmlns":34},"0 0 760 262","img","A GeoPackage file containing several layers, a style table and a spatial index, compared with a folder of shapefiles each made of four or more sidecar files","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[36,37,38],"title",{},"One container against a folder of sidecars",[40,41,42],"desc",{},"On the left a single GeoPackage file holds three vector layers, a stored style table and spatial indexes. On the right the same three layers as shapefiles require twelve files, carry a ten-character field name limit and an ambiguous encoding, and store no styles at all.",[44,45],"rect",{"x":46,"y":46,"width":47,"height":48,"fill":49},"0","760","262","#f6f3ea",[51,52,58],"text",{"x":53,"y":54,"style":55,"fill":56,"textAnchor":57},"380","28","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","Three layers, two very different deliverables",[44,60],{"x":61,"y":62,"width":63,"height":64,"rx":65,"fill":66,"stroke":67,"style":68},"20","48","340","188","10","#edf8e9","#15803d","stroke-width:2.5",[51,70,74],{"x":71,"y":72,"style":73,"fill":67,"textAnchor":57},"190","76","text-anchor:middle;font-size:12px;font-weight:bold;font-family:sans-serif","analysis.gpkg — one file",[44,76],{"x":77,"y":78,"width":79,"height":80,"rx":81,"fill":82,"stroke":83,"style":84},"44","90","292","30","5","#fffdf7","#59645f","stroke-width:1.5",[51,86,90],{"x":71,"y":87,"style":88,"fill":89,"textAnchor":57},"110","text-anchor:middle;font-size:11px;font-family:sans-serif","#2f3b35","flood_zones · MultiPolygon · EPSG:27700",[44,92],{"x":77,"y":93,"width":79,"height":80,"rx":81,"fill":82,"stroke":83,"style":84},"126",[51,95,97],{"x":71,"y":96,"style":88,"fill":89,"textAnchor":57},"146","gauges · Point · EPSG:27700",[44,99],{"x":77,"y":100,"width":79,"height":80,"rx":81,"fill":82,"stroke":83,"style":84},"162",[51,102,104],{"x":71,"y":103,"style":88,"fill":89,"textAnchor":57},"182","layer_styles · saved symbology",[51,106,108],{"x":71,"y":107,"style":88,"fill":67,"textAnchor":57},"216","field names of any length, UTF-8 throughout",[44,110],{"x":111,"y":62,"width":63,"height":64,"rx":65,"fill":112,"stroke":113,"style":68},"400","#fdf2e2","#b45309",[51,115,117],{"x":116,"y":72,"style":73,"fill":113,"textAnchor":57},"570","the same data as shapefiles",[44,119],{"x":120,"y":78,"width":79,"height":80,"rx":81,"fill":82,"stroke":83,"style":84},"424",[51,122,123],{"x":116,"y":87,"style":88,"fill":89,"textAnchor":57},"flood_zones .shp .shx .dbf .prj .cpg",[44,125],{"x":120,"y":93,"width":79,"height":80,"rx":81,"fill":82,"stroke":83,"style":84},[51,127,128],{"x":116,"y":96,"style":88,"fill":89,"textAnchor":57},"gauges .shp .shx .dbf .prj .cpg",[44,130],{"x":120,"y":100,"width":79,"height":80,"rx":81,"fill":82,"stroke":131,"style":84},"#b91c1c",[51,133,134],{"x":116,"y":103,"style":88,"fill":131,"textAnchor":57},"no styles, no long field names",[51,136,137],{"x":116,"y":107,"style":88,"fill":113,"textAnchor":57},"one missing sidecar and the layer is unreadable",[139,140,142],"h2",{"id":141},"prerequisites","Prerequisites",[144,145,146,159,166],"ul",{},[147,148,149,153,154,158],"li",{},[150,151,152],"strong",{},"QGIS 3.34 LTR"," (bundled Python 3.12) or newer. ",[155,156,157],"code",{},"writeAsVectorFormatV3"," exists from 3.20 onward.",[147,160,161,162,165],{},"A valid source layer — a Processing output, a memory layer, or anything else that answers ",[155,163,164],{},"isValid()",".",[147,167,168],{},"Write permission on the target directory, and enough space for the output.",[139,170,172],{"id":171},"write-a-layer-to-a-new-geopackage","Write a layer to a new GeoPackage",[174,175,180],"pre",{"className":176,"code":177,"language":178,"meta":179,"style":179},"language-python shiki shiki-themes github-dark","from qgis.core import (\n    QgsVectorFileWriter,\n    QgsCoordinateTransformContext,\n    QgsProject,\n)\n\noptions = QgsVectorFileWriter.SaveVectorOptions()\noptions.driverName = \"GPKG\"\noptions.layerName = \"flood_zones\"\noptions.fileEncoding = \"UTF-8\"\n\nerror, message, new_file, new_layer = QgsVectorFileWriter.writeAsVectorFormatV3(\n    layer,\n    \"\u002Fdata\u002Foutputs\u002Fanalysis.gpkg\",\n    QgsProject.instance().transformContext(),\n    options,\n)\n\nif error != QgsVectorFileWriter.NoError:\n    raise RuntimeError(f\"write failed ({error}): {message}\")\nprint(\"wrote\", new_layer or options.layerName, \"to\", new_file or \"analysis.gpkg\")\n","python","",[155,181,182,201,207,213,219,225,232,244,256,267,278,283,294,300,309,315,321,326,331,346,389],{"__ignoreMap":179},[183,184,187,191,195,198],"span",{"class":185,"line":186},"line",1,[183,188,190],{"class":189},"snl16","from",[183,192,194],{"class":193},"s95oV"," qgis.core ",[183,196,197],{"class":189},"import",[183,199,200],{"class":193}," (\n",[183,202,204],{"class":185,"line":203},2,[183,205,206],{"class":193},"    QgsVectorFileWriter,\n",[183,208,210],{"class":185,"line":209},3,[183,211,212],{"class":193},"    QgsCoordinateTransformContext,\n",[183,214,216],{"class":185,"line":215},4,[183,217,218],{"class":193},"    QgsProject,\n",[183,220,222],{"class":185,"line":221},5,[183,223,224],{"class":193},")\n",[183,226,228],{"class":185,"line":227},6,[183,229,231],{"emptyLinePlaceholder":230},true,"\n",[183,233,235,238,241],{"class":185,"line":234},7,[183,236,237],{"class":193},"options ",[183,239,240],{"class":189},"=",[183,242,243],{"class":193}," QgsVectorFileWriter.SaveVectorOptions()\n",[183,245,247,250,252],{"class":185,"line":246},8,[183,248,249],{"class":193},"options.driverName ",[183,251,240],{"class":189},[183,253,255],{"class":254},"sU2Wk"," \"GPKG\"\n",[183,257,259,262,264],{"class":185,"line":258},9,[183,260,261],{"class":193},"options.layerName ",[183,263,240],{"class":189},[183,265,266],{"class":254}," \"flood_zones\"\n",[183,268,270,273,275],{"class":185,"line":269},10,[183,271,272],{"class":193},"options.fileEncoding ",[183,274,240],{"class":189},[183,276,277],{"class":254}," \"UTF-8\"\n",[183,279,281],{"class":185,"line":280},11,[183,282,231],{"emptyLinePlaceholder":230},[183,284,286,289,291],{"class":185,"line":285},12,[183,287,288],{"class":193},"error, message, new_file, new_layer ",[183,290,240],{"class":189},[183,292,293],{"class":193}," QgsVectorFileWriter.writeAsVectorFormatV3(\n",[183,295,297],{"class":185,"line":296},13,[183,298,299],{"class":193},"    layer,\n",[183,301,303,306],{"class":185,"line":302},14,[183,304,305],{"class":254},"    \"\u002Fdata\u002Foutputs\u002Fanalysis.gpkg\"",[183,307,308],{"class":193},",\n",[183,310,312],{"class":185,"line":311},15,[183,313,314],{"class":193},"    QgsProject.instance().transformContext(),\n",[183,316,318],{"class":185,"line":317},16,[183,319,320],{"class":193},"    options,\n",[183,322,324],{"class":185,"line":323},17,[183,325,224],{"class":193},[183,327,329],{"class":185,"line":328},18,[183,330,231],{"emptyLinePlaceholder":230},[183,332,334,337,340,343],{"class":185,"line":333},19,[183,335,336],{"class":189},"if",[183,338,339],{"class":193}," error ",[183,341,342],{"class":189},"!=",[183,344,345],{"class":193}," QgsVectorFileWriter.NoError:\n",[183,347,349,352,356,359,362,365,368,371,374,377,379,382,384,387],{"class":185,"line":348},20,[183,350,351],{"class":189},"    raise",[183,353,355],{"class":354},"sDLfK"," RuntimeError",[183,357,358],{"class":193},"(",[183,360,361],{"class":189},"f",[183,363,364],{"class":254},"\"write failed (",[183,366,367],{"class":354},"{",[183,369,370],{"class":193},"error",[183,372,373],{"class":354},"}",[183,375,376],{"class":254},"): ",[183,378,367],{"class":354},[183,380,381],{"class":193},"message",[183,383,373],{"class":354},[183,385,386],{"class":254},"\"",[183,388,224],{"class":193},[183,390,392,395,397,400,403,406,409,412,415,417,420],{"class":185,"line":391},21,[183,393,394],{"class":354},"print",[183,396,358],{"class":193},[183,398,399],{"class":254},"\"wrote\"",[183,401,402],{"class":193},", new_layer ",[183,404,405],{"class":189},"or",[183,407,408],{"class":193}," options.layerName, ",[183,410,411],{"class":254},"\"to\"",[183,413,414],{"class":193},", new_file ",[183,416,405],{"class":189},[183,418,419],{"class":254}," \"analysis.gpkg\"",[183,421,224],{"class":193},[14,423,424,427,428,430,431,434,435,438],{},[150,425,426],{},"Breakdown:"," ",[155,429,157],{}," returns a four-tuple; the first element is an error code, and it is the only reliable indicator of success — the function does not raise on failure. Passing the project's transform context rather than a bare ",[155,432,433],{},"QgsCoordinateTransformContext()"," matters when the output CRS differs from the source, because that context carries the datum-transform choices configured for the project. ",[155,436,437],{},"layerName"," is the name the layer will carry inside the container, independent of the file name, which is what makes multi-layer containers possible.",[139,440,442],{"id":441},"add-a-second-layer-without-destroying-the-first","Add a second layer without destroying the first",[14,444,445,446,449],{},"This is the option that deserves a moment of attention. ",[155,447,448],{},"actionOnExistingFile"," decides what happens when the file already exists.",[174,451,453],{"className":176,"code":452,"language":178,"meta":179,"style":179},"options = QgsVectorFileWriter.SaveVectorOptions()\noptions.driverName = \"GPKG\"\noptions.layerName = \"gauges\"\noptions.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer\n",[155,454,455,463,471,480],{"__ignoreMap":179},[183,456,457,459,461],{"class":185,"line":186},[183,458,237],{"class":193},[183,460,240],{"class":189},[183,462,243],{"class":193},[183,464,465,467,469],{"class":185,"line":203},[183,466,249],{"class":193},[183,468,240],{"class":189},[183,470,255],{"class":254},[183,472,473,475,477],{"class":185,"line":209},[183,474,261],{"class":193},[183,476,240],{"class":189},[183,478,479],{"class":254}," \"gauges\"\n",[183,481,482,485,487],{"class":185,"line":215},[183,483,484],{"class":193},"options.actionOnExistingFile ",[183,486,240],{"class":189},[183,488,489],{"class":193}," QgsVectorFileWriter.CreateOrOverwriteLayer\n",[14,491,492,427,494,497,498,501,502,505,506,509],{},[150,493,426],{},[155,495,496],{},"CreateOrOverwriteLayer"," replaces just the named layer and leaves every other layer in the container untouched — this is what a nightly job that refreshes one dataset should use. ",[155,499,500],{},"CreateOrOverwriteFile"," (the default) deletes the whole GeoPackage and starts again, taking every other layer and every saved style with it. ",[155,503,504],{},"AppendToLayerNoNewFields"," adds rows to an existing layer and refuses new columns; ",[155,507,508],{},"AppendToLayerAddFields"," adds rows and widens the schema where needed.",[14,511,512],{},[29,513,516,519,522,525,528,533,538,544,549,553,556,560,564,567,569,571,574,577,581,584,588,591,597,601,605,608,612,616,619,622,628,632,636,638,641,644,647],{"viewBox":514,"role":32,"ariaLabel":515,"xmlns":34},"0 0 760 268","Four values of actionOnExistingFile shown against a container holding three layers, showing which layers survive each one",[36,517,518],{},"What each actionOnExistingFile value destroys",[40,520,521],{},"A container holding three layers is shown four times. Create or overwrite file removes all three and leaves only the new layer. Create or overwrite layer replaces one layer and keeps the others. Append to layer with no new fields adds rows to one layer and keeps every column as it was. Append to layer adding fields adds rows and widens the schema.",[44,523],{"x":46,"y":46,"width":47,"height":524,"fill":49},"268",[51,526,527],{"x":53,"y":54,"style":55,"fill":56,"textAnchor":57},"Only one of these is safe to run against a shared container",[44,529],{"x":530,"y":62,"width":531,"height":532,"rx":65,"fill":112,"stroke":131,"style":68},"14","176","196",[51,534,500],{"x":535,"y":536,"style":537,"fill":131,"textAnchor":57},"102","72","text-anchor:middle;font-size:11px;font-weight:bold;font-family:sans-serif",[44,539],{"x":80,"y":540,"width":541,"height":542,"rx":543,"fill":82,"stroke":67,"style":84},"88","144","26","4",[51,545,548],{"x":535,"y":546,"style":547,"fill":89,"textAnchor":57},"106","text-anchor:middle;font-size:10px;font-family:sans-serif","new layer",[51,550,552],{"x":535,"y":551,"style":547,"fill":131,"textAnchor":57},"140","layer B deleted",[51,554,555],{"x":535,"y":100,"style":547,"fill":131,"textAnchor":57},"layer C deleted",[51,557,559],{"x":535,"y":558,"style":547,"fill":131,"textAnchor":57},"184","styles deleted",[51,561,563],{"x":535,"y":562,"style":547,"fill":89,"textAnchor":57},"220","the default",[44,565],{"x":566,"y":62,"width":531,"height":532,"rx":65,"fill":66,"stroke":67,"style":68},"204",[51,568,496],{"x":79,"y":536,"style":537,"fill":67,"textAnchor":57},[44,570],{"x":562,"y":540,"width":541,"height":542,"rx":543,"fill":82,"stroke":67,"style":84},[51,572,573],{"x":79,"y":546,"style":547,"fill":89,"textAnchor":57},"layer A replaced",[44,575],{"x":562,"y":576,"width":541,"height":542,"rx":543,"fill":82,"stroke":83,"style":84},"120",[51,578,580],{"x":79,"y":579,"style":547,"fill":89,"textAnchor":57},"138","layer B kept",[44,582],{"x":562,"y":583,"width":541,"height":542,"rx":543,"fill":82,"stroke":83,"style":84},"152",[51,585,587],{"x":79,"y":586,"style":547,"fill":89,"textAnchor":57},"170","layer C kept",[51,589,590],{"x":79,"y":562,"style":547,"fill":67,"textAnchor":57},"use this in scheduled jobs",[44,592],{"x":593,"y":62,"width":531,"height":532,"rx":65,"fill":594,"stroke":595,"style":596},"394","#eff3ff","#2563eb","stroke-width:2",[51,598,600],{"x":599,"y":536,"style":537,"fill":595,"textAnchor":57},"482","AppendNoNewFields",[44,602],{"x":603,"y":540,"width":541,"height":604,"rx":543,"fill":82,"stroke":83,"style":84},"410","42",[51,606,607],{"x":599,"y":546,"style":547,"fill":89,"textAnchor":57},"rows added",[51,609,611],{"x":599,"y":610,"style":547,"fill":89,"textAnchor":57},"122","schema frozen",[51,613,615],{"x":599,"y":614,"style":547,"fill":89,"textAnchor":57},"158","extra source fields",[51,617,618],{"x":599,"y":531,"style":547,"fill":113,"textAnchor":57},"silently dropped",[51,620,621],{"x":599,"y":562,"style":547,"fill":89,"textAnchor":57},"good for time series",[44,623],{"x":624,"y":62,"width":625,"height":532,"rx":65,"fill":626,"stroke":627,"style":596},"584","160","#eef7f4","#0f766e",[51,629,631],{"x":630,"y":536,"style":537,"fill":627,"textAnchor":57},"664","AppendAddFields",[44,633],{"x":634,"y":540,"width":635,"height":604,"rx":543,"fill":82,"stroke":83,"style":84},"600","128",[51,637,607],{"x":630,"y":546,"style":547,"fill":89,"textAnchor":57},[51,639,640],{"x":630,"y":610,"style":547,"fill":89,"textAnchor":57},"schema widened",[51,642,643],{"x":630,"y":614,"style":547,"fill":89,"textAnchor":57},"new columns appear",[51,645,646],{"x":630,"y":531,"style":547,"fill":89,"textAnchor":57},"on old rows as null",[51,648,649],{"x":630,"y":562,"style":547,"fill":89,"textAnchor":57},"use with care",[139,651,653],{"id":652},"shape-the-output-on-the-way-out","Shape the output on the way out",[14,655,656],{},"The options object also decides which fields, which features and which CRS the output carries — doing this in the writer avoids creating an intermediate layer just to drop three columns.",[174,658,660],{"className":176,"code":659,"language":178,"meta":179,"style":179},"from qgis.core import QgsCoordinateReferenceSystem\n\noptions.attributes = [\n    layer.fields().indexOf(\"ward_name\"),\n    layer.fields().indexOf(\"incidents\"),\n]\noptions.ct = None\noptions.destCRS = QgsCoordinateReferenceSystem(\"EPSG:4326\")\noptions.onlySelectedFeatures = False\noptions.layerOptions = [\"FID=fid\", \"SPATIAL_INDEX=YES\"]\n",[155,661,662,673,677,687,698,707,712,722,737,747],{"__ignoreMap":179},[183,663,664,666,668,670],{"class":185,"line":186},[183,665,190],{"class":189},[183,667,194],{"class":193},[183,669,197],{"class":189},[183,671,672],{"class":193}," QgsCoordinateReferenceSystem\n",[183,674,675],{"class":185,"line":203},[183,676,231],{"emptyLinePlaceholder":230},[183,678,679,682,684],{"class":185,"line":209},[183,680,681],{"class":193},"options.attributes ",[183,683,240],{"class":189},[183,685,686],{"class":193}," [\n",[183,688,689,692,695],{"class":185,"line":215},[183,690,691],{"class":193},"    layer.fields().indexOf(",[183,693,694],{"class":254},"\"ward_name\"",[183,696,697],{"class":193},"),\n",[183,699,700,702,705],{"class":185,"line":221},[183,701,691],{"class":193},[183,703,704],{"class":254},"\"incidents\"",[183,706,697],{"class":193},[183,708,709],{"class":185,"line":227},[183,710,711],{"class":193},"]\n",[183,713,714,717,719],{"class":185,"line":234},[183,715,716],{"class":193},"options.ct ",[183,718,240],{"class":189},[183,720,721],{"class":354}," None\n",[183,723,724,727,729,732,735],{"class":185,"line":246},[183,725,726],{"class":193},"options.destCRS ",[183,728,240],{"class":189},[183,730,731],{"class":193}," QgsCoordinateReferenceSystem(",[183,733,734],{"class":254},"\"EPSG:4326\"",[183,736,224],{"class":193},[183,738,739,742,744],{"class":185,"line":258},[183,740,741],{"class":193},"options.onlySelectedFeatures ",[183,743,240],{"class":189},[183,745,746],{"class":354}," False\n",[183,748,749,752,754,757,760,763,766],{"class":185,"line":269},[183,750,751],{"class":193},"options.layerOptions ",[183,753,240],{"class":189},[183,755,756],{"class":193}," [",[183,758,759],{"class":254},"\"FID=fid\"",[183,761,762],{"class":193},", ",[183,764,765],{"class":254},"\"SPATIAL_INDEX=YES\"",[183,767,711],{"class":193},[14,769,770,427,772,775,776,779,780,784,785,788],{},[150,771,426],{},[155,773,774],{},"attributes"," is a list of field indexes to keep — everything else is omitted, which is the cheapest way to publish a subset. Setting ",[155,777,778],{},"destCRS"," reprojects during the write using the transform context passed to the writer, so no separate reprojection step is needed; see ",[21,781,783],{"href":782},"\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems\u002Ftransform-point-coordinates-pyqgis\u002F","Transform Point Coordinates in PyQGIS"," for what that transformation involves. ",[155,786,787],{},"layerOptions"," passes GDAL driver options straight through: naming the FID column keeps identifiers stable across rewrites, and the spatial index is what makes the result fast to read back.",[139,790,792],{"id":791},"save-the-style-with-the-data","Save the style with the data",[14,794,795],{},"A GeoPackage can carry symbology, so the recipient opens a styled map rather than grey polygons.",[174,797,799],{"className":176,"code":798,"language":178,"meta":179,"style":179},"layer_uri = \"\u002Fdata\u002Foutputs\u002Fanalysis.gpkg|layername=flood_zones\"\nwritten = QgsVectorLayer(layer_uri, \"Flood zones\", \"ogr\")\n\nwritten.loadNamedStyle(\"\u002Fdata\u002Fstyles\u002Fflood.qml\")\nwritten.saveStyleToDatabase(\n    name=\"flood_default\",\n    description=\"Depth bands, published 2026-08\",\n    useAsDefault=True,\n    uiFileContent=\"\",\n)\n",[155,800,801,811,831,835,845,850,863,875,887,899],{"__ignoreMap":179},[183,802,803,806,808],{"class":185,"line":186},[183,804,805],{"class":193},"layer_uri ",[183,807,240],{"class":189},[183,809,810],{"class":254}," \"\u002Fdata\u002Foutputs\u002Fanalysis.gpkg|layername=flood_zones\"\n",[183,812,813,816,818,821,824,826,829],{"class":185,"line":203},[183,814,815],{"class":193},"written ",[183,817,240],{"class":189},[183,819,820],{"class":193}," QgsVectorLayer(layer_uri, ",[183,822,823],{"class":254},"\"Flood zones\"",[183,825,762],{"class":193},[183,827,828],{"class":254},"\"ogr\"",[183,830,224],{"class":193},[183,832,833],{"class":185,"line":209},[183,834,231],{"emptyLinePlaceholder":230},[183,836,837,840,843],{"class":185,"line":215},[183,838,839],{"class":193},"written.loadNamedStyle(",[183,841,842],{"class":254},"\"\u002Fdata\u002Fstyles\u002Fflood.qml\"",[183,844,224],{"class":193},[183,846,847],{"class":185,"line":221},[183,848,849],{"class":193},"written.saveStyleToDatabase(\n",[183,851,852,856,858,861],{"class":185,"line":227},[183,853,855],{"class":854},"s9osk","    name",[183,857,240],{"class":189},[183,859,860],{"class":254},"\"flood_default\"",[183,862,308],{"class":193},[183,864,865,868,870,873],{"class":185,"line":234},[183,866,867],{"class":854},"    description",[183,869,240],{"class":189},[183,871,872],{"class":254},"\"Depth bands, published 2026-08\"",[183,874,308],{"class":193},[183,876,877,880,882,885],{"class":185,"line":246},[183,878,879],{"class":854},"    useAsDefault",[183,881,240],{"class":189},[183,883,884],{"class":354},"True",[183,886,308],{"class":193},[183,888,889,892,894,897],{"class":185,"line":258},[183,890,891],{"class":854},"    uiFileContent",[183,893,240],{"class":189},[183,895,896],{"class":254},"\"\"",[183,898,308],{"class":193},[183,900,901],{"class":185,"line":269},[183,902,224],{"class":193},[14,904,905,427,907,910,911,914,915,918,919,165],{},[150,906,426],{},[155,908,909],{},"saveStyleToDatabase()"," writes into the container's ",[155,912,913],{},"layer_styles"," table, which QGIS creates on first use. ",[155,916,917],{},"useAsDefault=True"," means the style is applied automatically whenever the layer is opened — the difference between a deliverable and a data dump. The same mechanism works against PostGIS, which is how a team shares one canonical symbology; the file-based alternative is covered in ",[21,920,922],{"href":921},"\u002Fpyqgis-cartography-visualization\u002Fprogrammatic-layer-styling\u002Fsave-and-load-qml-style-pyqgis\u002F","Save and Load a QML Style in PyQGIS",[139,924,926],{"id":925},"let-processing-write-into-the-container-directly","Let Processing write into the container directly",[14,928,929],{},"The writer is the explicit route, but most outputs come out of a Processing chain — and an algorithm can target a layer inside a GeoPackage without a separate write step.",[174,931,933],{"className":176,"code":932,"language":178,"meta":179,"style":179},"import processing\n\ncontainer = \"\u002Fdata\u002Foutputs\u002Fanalysis.gpkg\"\n\nprocessing.run(\"native:buffer\", {\n    \"INPUT\": \"\u002Fdata\u002Froads.gpkg|layername=roads\",\n    \"DISTANCE\": 25,\n    \"DISSOLVE\": True,\n    \"OUTPUT\": f\"ogr:dbname='{container}' table=\\\"road_buffers\\\" (geom)\",\n})\n",[155,934,935,942,946,956,960,971,984,996,1007,1042],{"__ignoreMap":179},[183,936,937,939],{"class":185,"line":186},[183,938,197],{"class":189},[183,940,941],{"class":193}," processing\n",[183,943,944],{"class":185,"line":203},[183,945,231],{"emptyLinePlaceholder":230},[183,947,948,951,953],{"class":185,"line":209},[183,949,950],{"class":193},"container ",[183,952,240],{"class":189},[183,954,955],{"class":254}," \"\u002Fdata\u002Foutputs\u002Fanalysis.gpkg\"\n",[183,957,958],{"class":185,"line":215},[183,959,231],{"emptyLinePlaceholder":230},[183,961,962,965,968],{"class":185,"line":221},[183,963,964],{"class":193},"processing.run(",[183,966,967],{"class":254},"\"native:buffer\"",[183,969,970],{"class":193},", {\n",[183,972,973,976,979,982],{"class":185,"line":227},[183,974,975],{"class":254},"    \"INPUT\"",[183,977,978],{"class":193},": ",[183,980,981],{"class":254},"\"\u002Fdata\u002Froads.gpkg|layername=roads\"",[183,983,308],{"class":193},[183,985,986,989,991,994],{"class":185,"line":234},[183,987,988],{"class":254},"    \"DISTANCE\"",[183,990,978],{"class":193},[183,992,993],{"class":354},"25",[183,995,308],{"class":193},[183,997,998,1001,1003,1005],{"class":185,"line":246},[183,999,1000],{"class":254},"    \"DISSOLVE\"",[183,1002,978],{"class":193},[183,1004,884],{"class":354},[183,1006,308],{"class":193},[183,1008,1009,1012,1014,1016,1019,1021,1024,1026,1029,1032,1035,1037,1040],{"class":185,"line":258},[183,1010,1011],{"class":254},"    \"OUTPUT\"",[183,1013,978],{"class":193},[183,1015,361],{"class":189},[183,1017,1018],{"class":254},"\"ogr:dbname='",[183,1020,367],{"class":354},[183,1022,1023],{"class":193},"container",[183,1025,373],{"class":354},[183,1027,1028],{"class":254},"' table=",[183,1030,1031],{"class":354},"\\\"",[183,1033,1034],{"class":254},"road_buffers",[183,1036,1031],{"class":354},[183,1038,1039],{"class":254}," (geom)\"",[183,1041,308],{"class":193},[183,1043,1044],{"class":185,"line":269},[183,1045,1046],{"class":193},"})\n",[14,1048,1049,1051,1052,1055,1056,1058],{},[150,1050,426],{}," The ",[155,1053,1054],{},"ogr:"," output syntax names the container, the layer inside it and the geometry column, so the algorithm writes straight into the GeoPackage — no temporary file, no second pass. Each algorithm run adds or replaces its own layer and leaves the others alone, which is the same guarantee ",[155,1057,496],{}," gives the writer. Quoting matters: the container path is in single quotes and the table name in double quotes, because the string is parsed by GDAL rather than by Python.",[14,1060,1061],{},"For a chain that produces several outputs, this turns a pipeline into a single self-describing deliverable — inputs, intermediates worth keeping and final results all in one file, each named for what it is:",[174,1063,1065],{"className":176,"code":1064,"language":178,"meta":179,"style":179},"outputs = {\n    \"flood_zones\": flood_result,\n    \"affected_parcels\": parcels_result,\n    \"gauges\": gauges_result,\n}\nfor name, layer in outputs.items():\n    options.layerName = name\n    options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer\n    QgsVectorFileWriter.writeAsVectorFormatV3(\n        layer, container, QgsProject.instance().transformContext(), options\n    )\n",[155,1066,1067,1077,1085,1093,1101,1106,1120,1130,1139,1144,1149],{"__ignoreMap":179},[183,1068,1069,1072,1074],{"class":185,"line":186},[183,1070,1071],{"class":193},"outputs ",[183,1073,240],{"class":189},[183,1075,1076],{"class":193}," {\n",[183,1078,1079,1082],{"class":185,"line":203},[183,1080,1081],{"class":254},"    \"flood_zones\"",[183,1083,1084],{"class":193},": flood_result,\n",[183,1086,1087,1090],{"class":185,"line":209},[183,1088,1089],{"class":254},"    \"affected_parcels\"",[183,1091,1092],{"class":193},": parcels_result,\n",[183,1094,1095,1098],{"class":185,"line":215},[183,1096,1097],{"class":254},"    \"gauges\"",[183,1099,1100],{"class":193},": gauges_result,\n",[183,1102,1103],{"class":185,"line":221},[183,1104,1105],{"class":193},"}\n",[183,1107,1108,1111,1114,1117],{"class":185,"line":227},[183,1109,1110],{"class":189},"for",[183,1112,1113],{"class":193}," name, layer ",[183,1115,1116],{"class":189},"in",[183,1118,1119],{"class":193}," outputs.items():\n",[183,1121,1122,1125,1127],{"class":185,"line":234},[183,1123,1124],{"class":193},"    options.layerName ",[183,1126,240],{"class":189},[183,1128,1129],{"class":193}," name\n",[183,1131,1132,1135,1137],{"class":185,"line":246},[183,1133,1134],{"class":193},"    options.actionOnExistingFile ",[183,1136,240],{"class":189},[183,1138,489],{"class":193},[183,1140,1141],{"class":185,"line":258},[183,1142,1143],{"class":193},"    QgsVectorFileWriter.writeAsVectorFormatV3(\n",[183,1145,1146],{"class":185,"line":269},[183,1147,1148],{"class":193},"        layer, container, QgsProject.instance().transformContext(), options\n",[183,1150,1151],{"class":185,"line":280},[183,1152,1153],{"class":193},"    )\n",[14,1155,1156,1158,1159,1162,1163,1166],{},[150,1157,426],{}," Reusing one options object across the loop is safe because each iteration sets the layer name before writing. The dictionary makes the container's contents obvious at a glance, and gives the recipient a file whose layer names document the analysis — considerably more useful than five files named ",[155,1160,1161],{},"output_1"," through ",[155,1164,1165],{},"output_5"," in a folder.",[14,1168,1169],{},[29,1170,1173,1176,1179,1182,1197,1200,1205,1210,1215,1219,1222,1226,1229,1233,1236,1239,1243,1246,1249,1251,1254,1256,1259,1262],{"viewBox":1171,"role":32,"ariaLabel":1172,"xmlns":34},"0 0 760 250","A processing chain writing every stage into one GeoPackage container, compared with the same chain scattering numbered temporary files across a folder",[36,1174,1175],{},"One container against a folder of numbered outputs",[40,1177,1178],{},"Three algorithm stages each write a named layer into a single GeoPackage, producing a deliverable whose layer names describe the analysis. The same chain writing to separate files produces a folder of numbered outputs whose meaning is lost as soon as the script is closed.",[44,1180],{"x":46,"y":46,"width":47,"height":1181,"fill":49},"250",[1183,1184,1185],"defs",{},[1186,1187,1193],"marker",{"id":1188,"viewBox":1189,"refX":1190,"refY":81,"markerWidth":1191,"markerHeight":1191,"orient":1192},"gpkgChainArrow","0 0 10 10","8","7","auto-start-reverse",[1194,1195],"path",{"d":1196,"fill":89},"M0 0 L10 5 L0 10 z",[51,1198,1199],{"x":53,"y":54,"style":55,"fill":56,"textAnchor":57},"The layer names are the documentation",[44,1201],{"x":1202,"y":1203,"width":1204,"height":531,"rx":65,"fill":66,"stroke":67,"style":68},"16","52","356",[51,1206,1209],{"x":1207,"y":1208,"style":73,"fill":67,"textAnchor":57},"194","78","analysis.gpkg",[44,1211],{"x":1212,"y":1213,"width":1214,"height":80,"rx":81,"fill":82,"stroke":83,"style":84},"40","94","308",[51,1216,1218],{"x":1207,"y":1217,"style":88,"fill":89,"textAnchor":57},"114","flood_zones",[44,1220],{"x":1212,"y":1221,"width":1214,"height":80,"rx":81,"fill":82,"stroke":83,"style":84},"130",[51,1223,1225],{"x":1207,"y":1224,"style":88,"fill":89,"textAnchor":57},"150","affected_parcels",[44,1227],{"x":1212,"y":1228,"width":1214,"height":80,"rx":81,"fill":82,"stroke":83,"style":84},"166",[51,1230,1232],{"x":1207,"y":1231,"style":88,"fill":89,"textAnchor":57},"186","gauges",[51,1234,1235],{"x":1207,"y":107,"style":88,"fill":67,"textAnchor":57},"one file to send, one to open",[44,1237],{"x":1238,"y":1203,"width":1204,"height":531,"rx":65,"fill":112,"stroke":113,"style":68},"388",[51,1240,1242],{"x":1241,"y":1208,"style":73,"fill":113,"textAnchor":57},"566","\u002Ftmp\u002Frun_2026_08\u002F",[44,1244],{"x":1245,"y":1213,"width":1214,"height":80,"rx":81,"fill":82,"stroke":83,"style":84},"412",[51,1247,1248],{"x":1241,"y":1217,"style":88,"fill":89,"textAnchor":57},"output_1.gpkg",[44,1250],{"x":1245,"y":1221,"width":1214,"height":80,"rx":81,"fill":82,"stroke":83,"style":84},[51,1252,1253],{"x":1241,"y":1224,"style":88,"fill":89,"textAnchor":57},"output_2.gpkg",[44,1255],{"x":1245,"y":1228,"width":1214,"height":80,"rx":81,"fill":82,"stroke":83,"style":84},[51,1257,1258],{"x":1241,"y":1231,"style":88,"fill":89,"textAnchor":57},"output_3.gpkg",[51,1260,1261],{"x":1241,"y":107,"style":88,"fill":113,"textAnchor":57},"which one was the parcels?",[185,1263],{"x1":1264,"y1":551,"x2":1265,"y2":551,"stroke":89,"style":1266},"372","384","stroke-width:2;marker-end:url(#gpkgChainArrow)",[139,1268,1270],{"id":1269},"qgis-version-compatibility","QGIS version compatibility",[14,1272,1273,1274,1276],{},"The examples target ",[150,1275,152],{}," (Python 3.12).",[1278,1279,1280,1296],"table",{},[1281,1282,1283],"thead",{},[1284,1285,1286,1290,1293],"tr",{},[1287,1288,1289],"th",{},"QGIS version",[1287,1291,1292],{},"Python",[1287,1294,1295],{},"Notes",[1297,1298,1299,1315,1328,1339],"tbody",{},[1284,1300,1301,1305,1308],{},[1302,1303,1304],"td",{},"3.16 LTR",[1302,1306,1307],{},"3.7",[1302,1309,1310,1311,1314],{},"Only ",[155,1312,1313],{},"writeAsVectorFormatV2",", which takes the same options object but returns a three-tuple.",[1284,1316,1317,1320,1323],{},[1302,1318,1319],{},"3.28 LTR",[1302,1321,1322],{},"3.9",[1302,1324,1325,1327],{},[155,1326,157],{}," available and recommended; identical options.",[1284,1329,1330,1333,1336],{},[1302,1331,1332],{},"3.34 LTR",[1302,1334,1335],{},"3.12",[1302,1337,1338],{},"Baseline for this page.",[1284,1340,1341,1344,1346],{},[1302,1342,1343],{},"3.40 \u002F 3.44",[1302,1345,1335],{},[1302,1347,1348],{},"V2 is deprecated and warns; V3 unchanged.",[14,1350,1351,1352,1355],{},"Where a script must run on both old and new releases, check for the attribute — ",[155,1353,1354],{},"hasattr(QgsVectorFileWriter, \"writeAsVectorFormatV3\")"," — and fall back rather than pinning the whole project to the older call.",[139,1357,1359],{"id":1358},"troubleshooting","Troubleshooting",[144,1361,1362,1372,1378,1388,1398,1404],{},[147,1363,1364,427,1367,1369,1370,165],{},[150,1365,1366],{},"The file exists but the earlier layers are gone.",[155,1368,448],{}," was left at its default. Use ",[155,1371,496],{},[147,1373,1374,1377],{},[150,1375,1376],{},"\"failed to create layer\" on a network drive."," SQLite needs real file locking, which SMB and NFS mounts often fake. Write locally and copy the finished file.",[147,1379,1380,1383,1384,165],{},[150,1381,1382],{},"Field names changed."," GeoPackage allows almost any name, but a source shapefile may already have truncated them. Rename before writing with the field-management patterns in ",[21,1385,1387],{"href":1386},"\u002Fspatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002Fadd-field-to-layer-pyqgis\u002F","Add a Field to a Layer in PyQGIS",[147,1389,1390,1393,1394,1397],{},[150,1391,1392],{},"The output is much larger than expected."," GeoPackage does not reclaim space automatically after overwrites. Run ",[155,1395,1396],{},"VACUUM"," through the ogr connection, or write to a fresh file.",[147,1399,1400,1403],{},[150,1401,1402],{},"Date fields arrive as text."," The source field type was string. Cast during the write by building the target fields explicitly, or fix the type at the source.",[147,1405,1406,1409,1410,1413],{},[150,1407,1408],{},"Nothing was written and the error code is 3."," That is ",[155,1411,1412],{},"ErrCreateDataSource"," — nearly always a path that does not exist. Create the directory first; the writer will not.",[139,1415,1417],{"id":1416},"conclusion","Conclusion",[14,1419,1420,1421,1423,1424,1426],{},"Writing to GeoPackage is one call plus one options object, and the option that matters most is ",[155,1422,448],{}," — ",[155,1425,496],{}," for anything that shares a container. Trim fields and reproject inside the writer rather than in an extra step, always test the returned error code, and save the style into the container so the file arrives ready to read.",[139,1428,1430],{"id":1429},"frequently-asked-questions","Frequently Asked Questions",[14,1432,1433,1436],{},[150,1434,1435],{},"Should I still use shapefiles for anything?","\nOnly when a downstream system demands one. GeoPackage removes the field-name limit, the encoding ambiguity, the 2 GB ceiling and the sidecar-file fragility in one move.",[14,1438,1439,1442,1443,1445],{},[150,1440,1441],{},"Can I write several layers in one transaction?","\nEach ",[155,1444,157],{}," call is its own transaction. To make a multi-layer publish atomic, write to a temporary file and rename it into place once every layer has been written.",[14,1447,1448,1451,1452,1454,1455,1458,1459,165],{},[150,1449,1450],{},"How do I append rows every night without duplicating them?","\nAppend with ",[155,1453,504],{}," and give the source a stable identifier column, or delete the day's rows first through the ogr connection's ",[155,1456,1457],{},"executeSql()",". The transactional version of this is covered in ",[21,1460,1462],{"href":1461},"\u002Fspatial-data-processing-automation\u002Fpostgis-and-database-workflows\u002Fappend-features-to-postgis-table-pyqgis\u002F","Append Features to a PostGIS Table in PyQGIS",[14,1464,1465,1468,1469,1472,1473,165],{},[150,1466,1467],{},"Does writing preserve the layer's filter or selection?","\nA subset string is honoured — the writer sees the filtered feature source. A selection is only used when ",[155,1470,1471],{},"onlySelectedFeatures"," is set to ",[155,1474,884],{},[14,1476,1477,1480,1481,1484,1485,1487],{},[150,1478,1479],{},"Why is the first read of a large GeoPackage slow?","\nUsually a missing spatial index. Pass ",[155,1482,1483],{},"SPATIAL_INDEX=YES"," in ",[155,1486,787],{},", or create it afterwards through the ogr connection.",[139,1489,1491],{"id":1490},"related","Related",[144,1493,1494,1499,1503,1509,1513],{},[147,1495,1496,1498],{},[21,1497,24],{"href":23}," — the guide this recipe belongs to",[147,1500,1501],{},[21,1502,1462],{"href":1461},[147,1504,1505],{},[21,1506,1508],{"href":1507},"\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002Fautomating-shapefile-to-geojson-conversion\u002F","Automating Shapefile to GeoJSON Conversion",[147,1510,1511],{},[21,1512,922],{"href":921},[147,1514,1515],{},[21,1516,1518],{"href":1517},"\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002F","Vector Data Manipulation in PyQGIS",[1520,1521,1522],"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":179,"searchDepth":203,"depth":203,"links":1524},[1525,1526,1527,1528,1529,1530,1531,1532,1533,1534,1535],{"id":141,"depth":203,"text":142},{"id":171,"depth":203,"text":172},{"id":441,"depth":203,"text":442},{"id":652,"depth":203,"text":653},{"id":791,"depth":203,"text":792},{"id":925,"depth":203,"text":926},{"id":1269,"depth":203,"text":1270},{"id":1358,"depth":203,"text":1359},{"id":1416,"depth":203,"text":1417},{"id":1429,"depth":203,"text":1430},{"id":1490,"depth":203,"text":1491},"Save results to GeoPackage with QgsVectorFileWriter.writeAsVectorFormatV3, add layers to an existing container without destroying it, and control field names, CRS and layer styles.","md",{"slug":1539,"type":1540,"breadcrumb":1541,"datePublished":1542,"dateModified":1542},"write-vector-layer-to-geopackage-pyqgis","article","Write to GeoPackage","2026-08-10","\u002Fspatial-data-processing-automation\u002Fpostgis-and-database-workflows\u002Fwrite-vector-layer-to-geopackage-pyqgis",{"title":5,"description":1536},"spatial-data-processing-automation\u002Fpostgis-and-database-workflows\u002Fwrite-vector-layer-to-geopackage-pyqgis\u002Findex","Psl7N4iwF-k9EF2HTQrC5netSJooT9Y717t7I3bARck",1786401338516]