[{"data":1,"prerenderedAt":1616},["ShallowReactive",2],{"doc:\u002Fpyqgis-cartography-visualization\u002Fprogrammatic-layer-styling\u002Fsave-styles-to-database-pyqgis":3},{"id":4,"title":5,"body":6,"description":1605,"extension":1606,"meta":1607,"navigation":247,"path":1612,"seo":1613,"stem":1614,"__hash__":1615},"docs\u002Fpyqgis-cartography-visualization\u002Fprogrammatic-layer-styling\u002Fsave-styles-to-database-pyqgis\u002Findex.md","Save Layer Styles to a Database in PyQGIS",{"type":7,"value":8,"toc":1591},"minimark",[9,13,22,31,172,177,202,206,215,394,404,408,414,487,567,582,586,589,727,740,744,747,813,1000,1028,1032,1038,1253,1258,1262,1265,1452,1457,1461,1477,1481,1513,1517,1526,1530,1536,1542,1548,1554,1558,1587],[10,11,5],"h1",{"id":12},"save-layer-styles-to-a-database-in-pyqgis",[14,15,16,17,21],"p",{},"A carefully styled layer is only carefully styled in the project where the style was made. Open the same GeoPackage in a new project, or load the same PostGIS table on a colleague's machine, and it appears with a random colour. QML files next to the data help, but they get separated from it. GeoPackage and PostGIS can store QGIS styles in the database itself, in a ",[18,19,20],"code",{},"layer_styles"," table, so the style travels with the data — and a style marked as default loads automatically whenever anyone adds the layer.",[14,23,24,25,30],{},"This recipe belongs to ",[26,27,29],"a",{"href":28},"\u002Fpyqgis-cartography-visualization\u002Fprogrammatic-layer-styling\u002F","Programmatic Layer Styling",". It saves a style into a GeoPackage or PostGIS database, sets it as the default, stores several named styles per layer, lists and loads styles from Python, and styles a whole delivery so recipients see it as intended.",[14,32,33],{},[34,35,40,44,48,55,72,81,90,96,101,105,108,115,123,129,134,138,142,146,150,158,164,168],"svg",{"viewBox":36,"role":37,"ariaLabel":38,"xmlns":39},"0 0 760 254","img","Data tables and a layer_styles table in the same database, with default styles applied automatically when layers load","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[41,42,43],"title",{},"Styles stored with the data",[45,46,47],"desc",{},"A GeoPackage or PostGIS database holds data tables and a layer_styles table. Each row of layer_styles references a table and geometry column, holds the style as QML and SLD, a name, a description and a use-as-default flag. When QGIS loads a table, it looks for a default style row and applies it automatically. Other named styles can be chosen in the layer properties.",[49,50],"rect",{"x":51,"y":51,"width":52,"height":53,"fill":54},"0","760","254","#f6f3ea",[56,57,58],"defs",{},[59,60,67],"marker",{"id":61,"viewBox":62,"refX":63,"refY":64,"markerWidth":65,"markerHeight":65,"orient":66},"dbStylesArrow","0 0 10 10","8","5","7","auto-start-reverse",[68,69],"path",{"d":70,"fill":71},"M0 0 L10 5 L0 10 z","#2f3b35",[73,74,80],"text",{"x":75,"y":76,"style":77,"fill":78,"textAnchor":79},"380","28","text-anchor:middle;font-size:14px;font-family:sans-serif;font-weight:bold","#17211d","middle","The style lives next to the table it styles",[49,82],{"x":83,"y":84,"width":85,"height":86,"rx":63,"fill":87,"stroke":88,"style":89},"24","60","220","170","#fffdf7","#59645f","stroke-width:2",[73,91,95],{"x":92,"y":93,"style":94,"fill":78,"textAnchor":79},"134","103.78","text-anchor:middle;font-size:11.5px;font-family:sans-serif;font-weight:bold","database",[73,97,100],{"x":92,"y":98,"style":99,"fill":71,"textAnchor":79},"133.78","text-anchor:middle;font-size:10.5px;font-family:sans-serif","parcels",[73,102,104],{"x":92,"y":103,"style":99,"fill":71,"textAnchor":79},"163.78","roads",[73,106,20],{"x":92,"y":107,"style":99,"fill":71,"textAnchor":79},"193.78",[109,110],"line",{"x1":111,"y1":112,"x2":113,"y2":112,"stroke":71,"style":114},"244","145","286","stroke-width:1.8;marker-end:url(#dbStylesArrow)",[49,116],{"x":117,"y":118,"width":119,"height":120,"rx":63,"fill":121,"stroke":122,"style":89},"290","52","280","186","#eef7f4","#0f766e",[73,124,128],{"x":125,"y":126,"style":127,"fill":122,"textAnchor":79},"430","92.6","text-anchor:middle;font-size:11.0px;font-family:sans-serif;font-weight:bold","layer_styles row",[73,130,133],{"x":125,"y":131,"style":132,"fill":71,"textAnchor":79},"120.6","text-anchor:middle;font-size:10.0px;font-family:sans-serif","f_table_name = parcels",[73,135,137],{"x":125,"y":136,"style":132,"fill":71,"textAnchor":79},"148.6","stylename, description",[73,139,141],{"x":125,"y":140,"style":132,"fill":71,"textAnchor":79},"176.6","styleQML, styleSLD",[73,143,145],{"x":125,"y":144,"style":132,"fill":71,"textAnchor":79},"204.6","useasdefault = true",[109,147],{"x1":148,"y1":112,"x2":149,"y2":112,"stroke":71,"style":114},"570","612",[49,151],{"x":152,"y":153,"width":154,"height":155,"rx":63,"fill":156,"stroke":157,"style":89},"616","90","120","110","#e8efe6","#15803d",[73,159,163],{"x":160,"y":161,"style":94,"fill":162,"textAnchor":79},"676","122.78","#166534","QGIS",[73,165,167],{"x":160,"y":166,"style":99,"fill":88,"textAnchor":79},"148.78","auto-applies",[73,169,171],{"x":160,"y":170,"style":99,"fill":88,"textAnchor":79},"174.78","default",[173,174,176],"h2",{"id":175},"prerequisites","Prerequisites",[178,179,180,184,192],"ul",{},[181,182,183],"li",{},"QGIS 3.34 LTR or newer, or the QGIS 4 series.",[181,185,186,187,191],{},"A layer from a GeoPackage, PostGIS, SpatiaLite or MS SQL source — formats that support stored styles. Shapefiles and CSVs do not; use QML files for those, as in ",[26,188,190],{"href":189},"\u002Fpyqgis-cartography-visualization\u002Fprogrammatic-layer-styling\u002Fsave-and-load-qml-style-pyqgis\u002F","saving and loading QML styles",".",[181,193,194,195,197,198,201],{},"For PostGIS, permission to create and write the ",[18,196,20],{}," table, usually in the ",[18,199,200],{},"public"," schema.",[173,203,205],{"id":204},"save-a-style-into-the-database","Save a style into the database",[14,207,208,211,212,214],{},[18,209,210],{},"saveStyleToDatabase"," writes the layer's current style as a row in the database's ",[18,213,20],{}," table, creating the table on first use.",[216,217,222],"pre",{"className":218,"code":219,"language":220,"meta":221,"style":221},"language-python shiki shiki-themes github-dark","from qgis.core import QgsProject, QgsVectorLayer\n\nparcels = QgsVectorLayer(\"\u002Fdata\u002Fcadastre\u002Fcadastre.gpkg|layername=parcels\", \"parcels\", \"ogr\")\n# ... style the layer: renderer, labels, form configuration ...\n\nerror = parcels.saveStyleToDatabase(\n    \"parcels default\",                   # style name\n    \"Cadastre parcels, ownership colours\", # description\n    True,                                # use as default\n    \"\")                                  # optional UI file (rarely used)\nprint(\"saved\" if not error else f\"failed: {error}\")\n","python","",[18,223,224,242,249,279,286,291,302,314,325,338,350],{"__ignoreMap":221},[225,226,228,232,236,239],"span",{"class":109,"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}," QgsProject, QgsVectorLayer\n",[225,243,245],{"class":109,"line":244},2,[225,246,248],{"emptyLinePlaceholder":247},true,"\n",[225,250,252,255,258,261,265,268,271,273,276],{"class":109,"line":251},3,[225,253,254],{"class":234},"parcels ",[225,256,257],{"class":230},"=",[225,259,260],{"class":234}," QgsVectorLayer(",[225,262,264],{"class":263},"sU2Wk","\"\u002Fdata\u002Fcadastre\u002Fcadastre.gpkg|layername=parcels\"",[225,266,267],{"class":234},", ",[225,269,270],{"class":263},"\"parcels\"",[225,272,267],{"class":234},[225,274,275],{"class":263},"\"ogr\"",[225,277,278],{"class":234},")\n",[225,280,282],{"class":109,"line":281},4,[225,283,285],{"class":284},"sjoCn","# ... style the layer: renderer, labels, form configuration ...\n",[225,287,289],{"class":109,"line":288},5,[225,290,248],{"emptyLinePlaceholder":247},[225,292,294,297,299],{"class":109,"line":293},6,[225,295,296],{"class":234},"error ",[225,298,257],{"class":230},[225,300,301],{"class":234}," parcels.saveStyleToDatabase(\n",[225,303,305,308,311],{"class":109,"line":304},7,[225,306,307],{"class":263},"    \"parcels default\"",[225,309,310],{"class":234},",                   ",[225,312,313],{"class":284},"# style name\n",[225,315,317,320,322],{"class":109,"line":316},8,[225,318,319],{"class":263},"    \"Cadastre parcels, ownership colours\"",[225,321,267],{"class":234},[225,323,324],{"class":284},"# description\n",[225,326,328,332,335],{"class":109,"line":327},9,[225,329,331],{"class":330},"sDLfK","    True",[225,333,334],{"class":234},",                                ",[225,336,337],{"class":284},"# use as default\n",[225,339,341,344,347],{"class":109,"line":340},10,[225,342,343],{"class":263},"    \"\"",[225,345,346],{"class":234},")                                  ",[225,348,349],{"class":284},"# optional UI file (rarely used)\n",[225,351,353,356,359,362,365,368,371,374,377,380,383,386,389,392],{"class":109,"line":352},11,[225,354,355],{"class":330},"print",[225,357,358],{"class":234},"(",[225,360,361],{"class":263},"\"saved\"",[225,363,364],{"class":230}," if",[225,366,367],{"class":230}," not",[225,369,370],{"class":234}," error ",[225,372,373],{"class":230},"else",[225,375,376],{"class":230}," f",[225,378,379],{"class":263},"\"failed: ",[225,381,382],{"class":330},"{",[225,384,385],{"class":234},"error",[225,387,388],{"class":330},"}",[225,390,391],{"class":263},"\"",[225,393,278],{"class":234},[14,395,396,400,401,403],{},[397,398,399],"strong",{},"Breakdown:"," The name identifies the style among others for the same table; the description appears in QGIS's style selector. Marking it as default means QGIS applies it automatically whenever this table is added to any project. The style is stored as full QML — renderer, labels, diagrams, forms, field aliases and more — plus an SLD version for other software. On recent releases ",[18,402,210],{}," returns an error message string (empty on success); older releases return nothing and report errors through the message log.",[173,405,407],{"id":406},"know-what-is-stored-and-where","Know what is stored, and where",[14,409,410,411,413],{},"The ",[18,412,20],{}," table is a normal table in the database, readable with SQL. Knowing its structure helps when managing styles at scale.",[14,415,416],{},[34,417,420,423,426,429,432,437,442,446,450,454,457,460,463,466,469,474,478,481,484],{"viewBox":418,"role":37,"ariaLabel":419,"xmlns":39},"0 0 760 240","Columns of the layer_styles table identifying the styled table, naming the style, holding QML and SLD, and recording default, owner and time",[41,421,422],{},"The layer_styles table",[45,424,425],{},"Key columns of layer_styles: f_table_catalog, f_table_schema, f_table_name and f_geometry_column identify the styled table; stylename and description describe the style; styleqml holds the QGIS style XML; stylesld holds an SLD version; useasdefault marks the default; owner and update_time record who saved it and when.",[49,427],{"x":51,"y":51,"width":52,"height":428,"fill":54},"240",[73,430,431],{"x":75,"y":76,"style":77,"fill":78,"textAnchor":79},"One row per style",[49,433],{"x":83,"y":434,"width":435,"height":436,"rx":63,"fill":87,"stroke":88,"style":89},"56","222","160",[73,438,441],{"x":439,"y":440,"style":127,"fill":78,"textAnchor":79},"135","97.6","which table",[73,443,445],{"x":439,"y":444,"style":132,"fill":71,"textAnchor":79},"125.6","f_table_schema",[73,447,449],{"x":439,"y":448,"style":132,"fill":71,"textAnchor":79},"153.6","f_table_name",[73,451,453],{"x":439,"y":452,"style":132,"fill":71,"textAnchor":79},"181.6","f_geometry_column",[49,455],{"x":456,"y":434,"width":435,"height":436,"rx":63,"fill":121,"stroke":122,"style":89},"269",[73,458,459],{"x":75,"y":440,"style":127,"fill":122,"textAnchor":79},"which style",[73,461,462],{"x":75,"y":444,"style":132,"fill":71,"textAnchor":79},"stylename",[73,464,465],{"x":75,"y":448,"style":132,"fill":71,"textAnchor":79},"description",[73,467,468],{"x":75,"y":452,"style":132,"fill":71,"textAnchor":79},"useasdefault",[49,470],{"x":471,"y":434,"width":435,"height":436,"rx":63,"fill":472,"stroke":473,"style":89},"514","#eff3ff","#2563eb",[73,475,477],{"x":476,"y":440,"style":127,"fill":473,"textAnchor":79},"625","content",[73,479,480],{"x":476,"y":444,"style":132,"fill":71,"textAnchor":79},"styleqml",[73,482,483],{"x":476,"y":448,"style":132,"fill":71,"textAnchor":79},"stylesld",[73,485,486],{"x":476,"y":452,"style":132,"fill":71,"textAnchor":79},"owner, update_time",[216,488,490],{"className":218,"code":489,"language":220,"meta":221,"style":221},"from qgis.core import QgsProviderRegistry\n\nmd = QgsProviderRegistry.instance().providerMetadata(\"ogr\")\nconn = md.createConnection(\"\u002Fdata\u002Fcadastre\u002Fcadastre.gpkg\", {})\nfor row in conn.executeSql(\n        \"SELECT f_table_name, stylename, useasdefault, update_time FROM layer_styles ORDER BY f_table_name\"):\n    print(row)\n",[18,491,492,503,507,521,537,551,559],{"__ignoreMap":221},[225,493,494,496,498,500],{"class":109,"line":227},[225,495,231],{"class":230},[225,497,235],{"class":234},[225,499,238],{"class":230},[225,501,502],{"class":234}," QgsProviderRegistry\n",[225,504,505],{"class":109,"line":244},[225,506,248],{"emptyLinePlaceholder":247},[225,508,509,512,514,517,519],{"class":109,"line":251},[225,510,511],{"class":234},"md ",[225,513,257],{"class":230},[225,515,516],{"class":234}," QgsProviderRegistry.instance().providerMetadata(",[225,518,275],{"class":263},[225,520,278],{"class":234},[225,522,523,526,528,531,534],{"class":109,"line":281},[225,524,525],{"class":234},"conn ",[225,527,257],{"class":230},[225,529,530],{"class":234}," md.createConnection(",[225,532,533],{"class":263},"\"\u002Fdata\u002Fcadastre\u002Fcadastre.gpkg\"",[225,535,536],{"class":234},", {})\n",[225,538,539,542,545,548],{"class":109,"line":288},[225,540,541],{"class":230},"for",[225,543,544],{"class":234}," row ",[225,546,547],{"class":230},"in",[225,549,550],{"class":234}," conn.executeSql(\n",[225,552,553,556],{"class":109,"line":293},[225,554,555],{"class":263},"        \"SELECT f_table_name, stylename, useasdefault, update_time FROM layer_styles ORDER BY f_table_name\"",[225,557,558],{"class":234},"):\n",[225,560,561,564],{"class":109,"line":304},[225,562,563],{"class":330},"    print",[225,565,566],{"class":234},"(row)\n",[14,568,569,571,572,574,575,577,578,191],{},[397,570,399],{}," Each row ties a style to a table and geometry column. Because it is plain SQL, styles can be audited — which tables have defaults, when they were last changed — and copied between databases with ordinary inserts. In PostGIS, ",[18,573,20],{}," is usually created in the ",[18,576,200],{}," schema and shared by all schemas in the database, so give editors write access to it and readers select access. The connections API used here is covered in ",[26,579,581],{"href":580},"\u002Fspatial-data-processing-automation\u002Fpostgis-and-database-workflows\u002Fuse-database-connections-api-pyqgis\u002F","using the database connections API",[173,583,585],{"id":584},"store-several-named-styles","Store several named styles",[14,587,588],{},"One table often needs several looks: a detailed style for editing, a simple one for overview maps, a print style with heavier lines. Each can be saved under its own name, with one marked as default.",[216,590,592],{"className":218,"code":591,"language":220,"meta":221,"style":221},"from qgis.core import QgsSingleSymbolRenderer, QgsFillSymbol\n\nparcels.setRenderer(QgsSingleSymbolRenderer(QgsFillSymbol.createSimple(\n    {\"color\": \"255,255,255,0\", \"outline_color\": \"#59645f\", \"outline_width\": \"0.15\"})))\nparcels.setLabelsEnabled(False)\nparcels.saveStyleToDatabase(\"parcels overview\", \"Outlines only, no labels\", False, \"\")\n\ncount, ids, names, descriptions, error = parcels.listStylesInDatabase()\nfor sid, name, desc in zip(ids, names, descriptions):\n    print(sid, name, \"-\", desc)\n",[18,593,594,605,609,614,651,661,685,689,699,714],{"__ignoreMap":221},[225,595,596,598,600,602],{"class":109,"line":227},[225,597,231],{"class":230},[225,599,235],{"class":234},[225,601,238],{"class":230},[225,603,604],{"class":234}," QgsSingleSymbolRenderer, QgsFillSymbol\n",[225,606,607],{"class":109,"line":244},[225,608,248],{"emptyLinePlaceholder":247},[225,610,611],{"class":109,"line":251},[225,612,613],{"class":234},"parcels.setRenderer(QgsSingleSymbolRenderer(QgsFillSymbol.createSimple(\n",[225,615,616,619,622,625,628,630,633,635,638,640,643,645,648],{"class":109,"line":281},[225,617,618],{"class":234},"    {",[225,620,621],{"class":263},"\"color\"",[225,623,624],{"class":234},": ",[225,626,627],{"class":263},"\"255,255,255,0\"",[225,629,267],{"class":234},[225,631,632],{"class":263},"\"outline_color\"",[225,634,624],{"class":234},[225,636,637],{"class":263},"\"#59645f\"",[225,639,267],{"class":234},[225,641,642],{"class":263},"\"outline_width\"",[225,644,624],{"class":234},[225,646,647],{"class":263},"\"0.15\"",[225,649,650],{"class":234},"})))\n",[225,652,653,656,659],{"class":109,"line":288},[225,654,655],{"class":234},"parcels.setLabelsEnabled(",[225,657,658],{"class":330},"False",[225,660,278],{"class":234},[225,662,663,666,669,671,674,676,678,680,683],{"class":109,"line":293},[225,664,665],{"class":234},"parcels.saveStyleToDatabase(",[225,667,668],{"class":263},"\"parcels overview\"",[225,670,267],{"class":234},[225,672,673],{"class":263},"\"Outlines only, no labels\"",[225,675,267],{"class":234},[225,677,658],{"class":330},[225,679,267],{"class":234},[225,681,682],{"class":263},"\"\"",[225,684,278],{"class":234},[225,686,687],{"class":109,"line":304},[225,688,248],{"emptyLinePlaceholder":247},[225,690,691,694,696],{"class":109,"line":316},[225,692,693],{"class":234},"count, ids, names, descriptions, error ",[225,695,257],{"class":230},[225,697,698],{"class":234}," parcels.listStylesInDatabase()\n",[225,700,701,703,706,708,711],{"class":109,"line":327},[225,702,541],{"class":230},[225,704,705],{"class":234}," sid, name, desc ",[225,707,547],{"class":230},[225,709,710],{"class":330}," zip",[225,712,713],{"class":234},"(ids, names, descriptions):\n",[225,715,716,718,721,724],{"class":109,"line":340},[225,717,563],{"class":330},[225,719,720],{"class":234},"(sid, name, ",[225,722,723],{"class":263},"\"-\"",[225,725,726],{"class":234},", desc)\n",[14,728,729,731,732,735,736,739],{},[397,730,399],{}," Saving with ",[18,733,734],{},"useAsDefault=False"," stores an alternative without changing which style loads automatically. ",[18,737,738],{},"listStylesInDatabase"," returns the number of styles related to this table first — those for the same table come first in the lists — then parallel lists of ids, names and descriptions, and an error message. Users can pick a stored style in the layer properties' Style menu; scripts load one by id, as below.",[173,741,743],{"id":742},"load-a-stored-style-from-python","Load a stored style from Python",[14,745,746],{},"Loading by id applies a stored style to a layer, for example to switch every layer of a project into its print style before export.",[14,748,749],{},[34,750,753,756,759,762,769,772,774,778,782,787,791,795,798,802,806,810],{"viewBox":751,"role":37,"ariaLabel":752,"xmlns":39},"0 0 760 196","A script finding a stored style by name, loading its QML and applying it, repeated across layers to switch a project's look",[41,754,755],{},"Switching styles by name",[45,757,758],{},"A script lists the stored styles for a layer, finds the one named for print, loads its QML with getStyleFromDatabase and applies it with importNamedStyle. Repeating this for every layer switches a whole project to its print styles, and loading the default styles switches it back.",[49,760],{"x":51,"y":51,"width":52,"height":761,"fill":54},"196",[56,763,764],{},[59,765,767],{"id":766,"viewBox":62,"refX":63,"refY":64,"markerWidth":65,"markerHeight":65,"orient":66},"dbLoadArrow",[68,768],{"d":70,"fill":71},[73,770,771],{"x":75,"y":76,"style":77,"fill":78,"textAnchor":79},"Find by name, fetch, apply",[49,773],{"x":83,"y":84,"width":435,"height":154,"rx":63,"fill":87,"stroke":88,"style":89},[73,775,777],{"x":439,"y":776,"style":94,"fill":78,"textAnchor":79},"110.78","listStyles…",[73,779,781],{"x":439,"y":780,"style":99,"fill":88,"textAnchor":79},"136.78","ids and names",[109,783],{"x1":784,"y1":154,"x2":785,"y2":154,"stroke":71,"style":786},"246","288","stroke-width:1.8;marker-end:url(#dbLoadArrow)",[49,788],{"x":789,"y":84,"width":790,"height":154,"rx":63,"fill":121,"stroke":122,"style":89},"292","200",[73,792,794],{"x":793,"y":776,"style":94,"fill":122,"textAnchor":79},"392","getStyleFrom…",[73,796,797],{"x":793,"y":780,"style":99,"fill":88,"textAnchor":79},"QML by id",[109,799],{"x1":800,"y1":154,"x2":801,"y2":154,"stroke":71,"style":786},"492","534",[49,803],{"x":804,"y":84,"width":805,"height":154,"rx":63,"fill":156,"stroke":157,"style":89},"538","198",[73,807,809],{"x":808,"y":776,"style":94,"fill":162,"textAnchor":79},"637","importNamedStyle",[73,811,812],{"x":808,"y":780,"style":99,"fill":88,"textAnchor":79},"applied",[216,814,816],{"className":218,"code":815,"language":220,"meta":221,"style":221},"from qgis.PyQt.QtXml import QDomDocument\n\ndef apply_stored_style(layer, wanted_name):\n    count, ids, names, descs, err = layer.listStylesInDatabase()\n    for sid, name in zip(ids[:count], names[:count]):\n        if name == wanted_name:\n            qml, err = layer.getStyleFromDatabase(sid)\n            doc = QDomDocument()\n            doc.setContent(qml)\n            ok, msg = layer.importNamedStyle(doc)\n            layer.triggerRepaint()\n            return ok\n    return False\n\nfor lyr in QgsProject.instance().mapLayers().values():\n    if isinstance(lyr, QgsVectorLayer):\n        print(lyr.name(), apply_stored_style(lyr, f\"{lyr.name()} print\"))\n",[18,817,818,830,834,846,856,871,885,895,905,910,920,925,934,943,948,961,973],{"__ignoreMap":221},[225,819,820,822,825,827],{"class":109,"line":227},[225,821,231],{"class":230},[225,823,824],{"class":234}," qgis.PyQt.QtXml ",[225,826,238],{"class":230},[225,828,829],{"class":234}," QDomDocument\n",[225,831,832],{"class":109,"line":244},[225,833,248],{"emptyLinePlaceholder":247},[225,835,836,839,843],{"class":109,"line":251},[225,837,838],{"class":230},"def",[225,840,842],{"class":841},"svObZ"," apply_stored_style",[225,844,845],{"class":234},"(layer, wanted_name):\n",[225,847,848,851,853],{"class":109,"line":281},[225,849,850],{"class":234},"    count, ids, names, descs, err ",[225,852,257],{"class":230},[225,854,855],{"class":234}," layer.listStylesInDatabase()\n",[225,857,858,861,864,866,868],{"class":109,"line":288},[225,859,860],{"class":230},"    for",[225,862,863],{"class":234}," sid, name ",[225,865,547],{"class":230},[225,867,710],{"class":330},[225,869,870],{"class":234},"(ids[:count], names[:count]):\n",[225,872,873,876,879,882],{"class":109,"line":293},[225,874,875],{"class":230},"        if",[225,877,878],{"class":234}," name ",[225,880,881],{"class":230},"==",[225,883,884],{"class":234}," wanted_name:\n",[225,886,887,890,892],{"class":109,"line":304},[225,888,889],{"class":234},"            qml, err ",[225,891,257],{"class":230},[225,893,894],{"class":234}," layer.getStyleFromDatabase(sid)\n",[225,896,897,900,902],{"class":109,"line":316},[225,898,899],{"class":234},"            doc ",[225,901,257],{"class":230},[225,903,904],{"class":234}," QDomDocument()\n",[225,906,907],{"class":109,"line":327},[225,908,909],{"class":234},"            doc.setContent(qml)\n",[225,911,912,915,917],{"class":109,"line":340},[225,913,914],{"class":234},"            ok, msg ",[225,916,257],{"class":230},[225,918,919],{"class":234}," layer.importNamedStyle(doc)\n",[225,921,922],{"class":109,"line":352},[225,923,924],{"class":234},"            layer.triggerRepaint()\n",[225,926,928,931],{"class":109,"line":927},12,[225,929,930],{"class":230},"            return",[225,932,933],{"class":234}," ok\n",[225,935,937,940],{"class":109,"line":936},13,[225,938,939],{"class":230},"    return",[225,941,942],{"class":330}," False\n",[225,944,946],{"class":109,"line":945},14,[225,947,248],{"emptyLinePlaceholder":247},[225,949,951,953,956,958],{"class":109,"line":950},15,[225,952,541],{"class":230},[225,954,955],{"class":234}," lyr ",[225,957,547],{"class":230},[225,959,960],{"class":234}," QgsProject.instance().mapLayers().values():\n",[225,962,964,967,970],{"class":109,"line":963},16,[225,965,966],{"class":230},"    if",[225,968,969],{"class":330}," isinstance",[225,971,972],{"class":234},"(lyr, QgsVectorLayer):\n",[225,974,976,979,982,985,987,989,992,994,997],{"class":109,"line":975},17,[225,977,978],{"class":330},"        print",[225,980,981],{"class":234},"(lyr.name(), apply_stored_style(lyr, ",[225,983,984],{"class":230},"f",[225,986,391],{"class":263},[225,988,382],{"class":330},[225,990,991],{"class":234},"lyr.name()",[225,993,388],{"class":330},[225,995,996],{"class":263}," print\"",[225,998,999],{"class":234},"))\n",[14,1001,1002,1004,1005,1008,1009,1012,1013,1015,1016,1020,1021,1023,1024,191],{},[397,1003,399],{}," Restricting the search to the first ",[18,1006,1007],{},"count"," entries — styles for this table — avoids applying a same-named style from another table. ",[18,1010,1011],{},"getStyleFromDatabase"," returns the QML text; ",[18,1014,809],{}," applies it to the layer. Naming styles with a convention, such as \"",[1017,1018,1019],"em",{},"layer"," print\" and \"",[1017,1022,1019],{}," overview\", lets one loop switch a whole project between looks, which is useful before batch exports of ",[26,1025,1027],{"href":1026},"\u002Fspatial-data-processing-automation\u002Fautomated-map-layout-generation\u002Fexporting-multiple-qgis-layouts-to-pdf\u002F","print layouts",[173,1029,1031],{"id":1030},"copy-styles-between-databases","Copy styles between databases",[14,1033,1034,1035,1037],{},"Organisations often keep a reference database of approved styles and apply them to new databases as they are created — a fresh GeoPackage for each project, a new PostGIS schema for each year. Copying ",[18,1036,20],{}," rows between databases moves the whole style set in one step.",[216,1039,1041],{"className":218,"code":1040,"language":220,"meta":221,"style":221},"src = md.createConnection(\"\u002Fdata\u002Fstyles\u002Freference_styles.gpkg\", {})\ndst = md.createConnection(\"\u002Fdata\u002Fprojects\u002F2027\u002Fproject.gpkg\", {})\n\nrows = src.executeSql(\"SELECT f_table_name, f_geometry_column, stylename, styleqml, stylesld, \"\n                      \"useasdefault, description FROM layer_styles\")\ndst_tables = {t.tableName() for t in dst.tables()}\ncopied = 0\nfor table, geom, name, qml, sld, default, desc in rows:\n    if table not in dst_tables:\n        continue\n    lyr = QgsVectorLayer(f\"\u002Fdata\u002Fprojects\u002F2027\u002Fproject.gpkg|layername={table}\", table, \"ogr\")\n    doc = QDomDocument()\n    doc.setContent(qml)\n    lyr.importNamedStyle(doc)\n    lyr.saveStyleToDatabase(name, desc or \"\", bool(default), \"\")\n    copied += 1\nprint(copied, \"styles copied\")\n",[18,1042,1043,1057,1071,1075,1088,1095,1115,1125,1137,1153,1158,1188,1197,1202,1207,1230,1241],{"__ignoreMap":221},[225,1044,1045,1048,1050,1052,1055],{"class":109,"line":227},[225,1046,1047],{"class":234},"src ",[225,1049,257],{"class":230},[225,1051,530],{"class":234},[225,1053,1054],{"class":263},"\"\u002Fdata\u002Fstyles\u002Freference_styles.gpkg\"",[225,1056,536],{"class":234},[225,1058,1059,1062,1064,1066,1069],{"class":109,"line":244},[225,1060,1061],{"class":234},"dst ",[225,1063,257],{"class":230},[225,1065,530],{"class":234},[225,1067,1068],{"class":263},"\"\u002Fdata\u002Fprojects\u002F2027\u002Fproject.gpkg\"",[225,1070,536],{"class":234},[225,1072,1073],{"class":109,"line":251},[225,1074,248],{"emptyLinePlaceholder":247},[225,1076,1077,1080,1082,1085],{"class":109,"line":281},[225,1078,1079],{"class":234},"rows ",[225,1081,257],{"class":230},[225,1083,1084],{"class":234}," src.executeSql(",[225,1086,1087],{"class":263},"\"SELECT f_table_name, f_geometry_column, stylename, styleqml, stylesld, \"\n",[225,1089,1090,1093],{"class":109,"line":288},[225,1091,1092],{"class":263},"                      \"useasdefault, description FROM layer_styles\"",[225,1094,278],{"class":234},[225,1096,1097,1100,1102,1105,1107,1110,1112],{"class":109,"line":293},[225,1098,1099],{"class":234},"dst_tables ",[225,1101,257],{"class":230},[225,1103,1104],{"class":234}," {t.tableName() ",[225,1106,541],{"class":230},[225,1108,1109],{"class":234}," t ",[225,1111,547],{"class":230},[225,1113,1114],{"class":234}," dst.tables()}\n",[225,1116,1117,1120,1122],{"class":109,"line":304},[225,1118,1119],{"class":234},"copied ",[225,1121,257],{"class":230},[225,1123,1124],{"class":330}," 0\n",[225,1126,1127,1129,1132,1134],{"class":109,"line":316},[225,1128,541],{"class":230},[225,1130,1131],{"class":234}," table, geom, name, qml, sld, default, desc ",[225,1133,547],{"class":230},[225,1135,1136],{"class":234}," rows:\n",[225,1138,1139,1141,1144,1147,1150],{"class":109,"line":327},[225,1140,966],{"class":230},[225,1142,1143],{"class":234}," table ",[225,1145,1146],{"class":230},"not",[225,1148,1149],{"class":230}," in",[225,1151,1152],{"class":234}," dst_tables:\n",[225,1154,1155],{"class":109,"line":340},[225,1156,1157],{"class":230},"        continue\n",[225,1159,1160,1163,1165,1167,1169,1172,1174,1177,1179,1181,1184,1186],{"class":109,"line":352},[225,1161,1162],{"class":234},"    lyr ",[225,1164,257],{"class":230},[225,1166,260],{"class":234},[225,1168,984],{"class":230},[225,1170,1171],{"class":263},"\"\u002Fdata\u002Fprojects\u002F2027\u002Fproject.gpkg|layername=",[225,1173,382],{"class":330},[225,1175,1176],{"class":234},"table",[225,1178,388],{"class":330},[225,1180,391],{"class":263},[225,1182,1183],{"class":234},", table, ",[225,1185,275],{"class":263},[225,1187,278],{"class":234},[225,1189,1190,1193,1195],{"class":109,"line":927},[225,1191,1192],{"class":234},"    doc ",[225,1194,257],{"class":230},[225,1196,904],{"class":234},[225,1198,1199],{"class":109,"line":936},[225,1200,1201],{"class":234},"    doc.setContent(qml)\n",[225,1203,1204],{"class":109,"line":945},[225,1205,1206],{"class":234},"    lyr.importNamedStyle(doc)\n",[225,1208,1209,1212,1215,1218,1220,1223,1226,1228],{"class":109,"line":950},[225,1210,1211],{"class":234},"    lyr.saveStyleToDatabase(name, desc ",[225,1213,1214],{"class":230},"or",[225,1216,1217],{"class":263}," \"\"",[225,1219,267],{"class":234},[225,1221,1222],{"class":330},"bool",[225,1224,1225],{"class":234},"(default), ",[225,1227,682],{"class":263},[225,1229,278],{"class":234},[225,1231,1232,1235,1238],{"class":109,"line":963},[225,1233,1234],{"class":234},"    copied ",[225,1236,1237],{"class":230},"+=",[225,1239,1240],{"class":330}," 1\n",[225,1242,1243,1245,1248,1251],{"class":109,"line":975},[225,1244,355],{"class":330},[225,1246,1247],{"class":234},"(copied, ",[225,1249,1250],{"class":263},"\"styles copied\"",[225,1252,278],{"class":234},[14,1254,1255,1257],{},[397,1256,399],{}," Reading styles from the reference database and saving them through each destination layer — rather than inserting rows with SQL — lets QGIS fill in the destination's own table and geometry column references correctly. Tables without a matching table in the destination are skipped, so one reference database can serve projects that use only some of its layers. The same loop works between GeoPackage and PostGIS in either direction, since both are reached through connections and layers.",[173,1259,1261],{"id":1260},"style-a-whole-delivery","Style a whole delivery",[14,1263,1264],{},"When sending a GeoPackage to someone else, storing default styles for every table means they see it exactly as intended, in any QGIS, without a project file.",[216,1266,1268],{"className":218,"code":1267,"language":220,"meta":221,"style":221},"from pathlib import Path\n\ndelivery = \"\u002Fdata\u002Fdelivery\u002Fdistrict_plan_2026.gpkg\"\nstyles_dir = Path(\"\u002Fdata\u002Fstyles\u002Fdistrict_plan\")\nfor sub in QgsProviderRegistry.instance().providerMetadata(\"ogr\").querySublayers(delivery):\n    lyr = QgsVectorLayer(sub.uri(), sub.name(), \"ogr\")\n    qml = styles_dir \u002F f\"{sub.name()}.qml\"\n    if lyr.isValid() and qml.exists():\n        lyr.loadNamedStyle(str(qml))\n        err = lyr.saveStyleToDatabase(f\"{sub.name()} default\", \"District plan 2026\", True, \"\")\n        print(sub.name(), \"styled\" if not err else err)\n",[18,1269,1270,1282,1286,1296,1311,1327,1340,1367,1380,1391,1430],{"__ignoreMap":221},[225,1271,1272,1274,1277,1279],{"class":109,"line":227},[225,1273,231],{"class":230},[225,1275,1276],{"class":234}," pathlib ",[225,1278,238],{"class":230},[225,1280,1281],{"class":234}," Path\n",[225,1283,1284],{"class":109,"line":244},[225,1285,248],{"emptyLinePlaceholder":247},[225,1287,1288,1291,1293],{"class":109,"line":251},[225,1289,1290],{"class":234},"delivery ",[225,1292,257],{"class":230},[225,1294,1295],{"class":263}," \"\u002Fdata\u002Fdelivery\u002Fdistrict_plan_2026.gpkg\"\n",[225,1297,1298,1301,1303,1306,1309],{"class":109,"line":281},[225,1299,1300],{"class":234},"styles_dir ",[225,1302,257],{"class":230},[225,1304,1305],{"class":234}," Path(",[225,1307,1308],{"class":263},"\"\u002Fdata\u002Fstyles\u002Fdistrict_plan\"",[225,1310,278],{"class":234},[225,1312,1313,1315,1318,1320,1322,1324],{"class":109,"line":288},[225,1314,541],{"class":230},[225,1316,1317],{"class":234}," sub ",[225,1319,547],{"class":230},[225,1321,516],{"class":234},[225,1323,275],{"class":263},[225,1325,1326],{"class":234},").querySublayers(delivery):\n",[225,1328,1329,1331,1333,1336,1338],{"class":109,"line":293},[225,1330,1162],{"class":234},[225,1332,257],{"class":230},[225,1334,1335],{"class":234}," QgsVectorLayer(sub.uri(), sub.name(), ",[225,1337,275],{"class":263},[225,1339,278],{"class":234},[225,1341,1342,1345,1347,1350,1353,1355,1357,1359,1362,1364],{"class":109,"line":304},[225,1343,1344],{"class":234},"    qml ",[225,1346,257],{"class":230},[225,1348,1349],{"class":234}," styles_dir ",[225,1351,1352],{"class":230},"\u002F",[225,1354,376],{"class":230},[225,1356,391],{"class":263},[225,1358,382],{"class":330},[225,1360,1361],{"class":234},"sub.name()",[225,1363,388],{"class":330},[225,1365,1366],{"class":263},".qml\"\n",[225,1368,1369,1371,1374,1377],{"class":109,"line":316},[225,1370,966],{"class":230},[225,1372,1373],{"class":234}," lyr.isValid() ",[225,1375,1376],{"class":230},"and",[225,1378,1379],{"class":234}," qml.exists():\n",[225,1381,1382,1385,1388],{"class":109,"line":327},[225,1383,1384],{"class":234},"        lyr.loadNamedStyle(",[225,1386,1387],{"class":330},"str",[225,1389,1390],{"class":234},"(qml))\n",[225,1392,1393,1396,1398,1401,1403,1405,1407,1409,1411,1414,1416,1419,1421,1424,1426,1428],{"class":109,"line":340},[225,1394,1395],{"class":234},"        err ",[225,1397,257],{"class":230},[225,1399,1400],{"class":234}," lyr.saveStyleToDatabase(",[225,1402,984],{"class":230},[225,1404,391],{"class":263},[225,1406,382],{"class":330},[225,1408,1361],{"class":234},[225,1410,388],{"class":330},[225,1412,1413],{"class":263}," default\"",[225,1415,267],{"class":234},[225,1417,1418],{"class":263},"\"District plan 2026\"",[225,1420,267],{"class":234},[225,1422,1423],{"class":330},"True",[225,1425,267],{"class":234},[225,1427,682],{"class":263},[225,1429,278],{"class":234},[225,1431,1432,1434,1437,1440,1442,1444,1447,1449],{"class":109,"line":352},[225,1433,978],{"class":330},[225,1435,1436],{"class":234},"(sub.name(), ",[225,1438,1439],{"class":263},"\"styled\"",[225,1441,364],{"class":230},[225,1443,367],{"class":230},[225,1445,1446],{"class":234}," err ",[225,1448,373],{"class":230},[225,1450,1451],{"class":234}," err)\n",[14,1453,1454,1456],{},[397,1455,399],{}," Each table in the GeoPackage gets the QML maintained for it, saved into the package as its default style. Recipients who drag the GeoPackage into QGIS see every layer styled immediately. Keeping the QML files under version control and writing them into deliveries with a script means the styles in deliveries are always the reviewed ones, not whatever happened to be on someone's screen.",[173,1458,1460],{"id":1459},"qgis-version-compatibility","QGIS version compatibility",[14,1462,1463,267,1465,267,1467,1469,1470,1472,1473,1476],{},[18,1464,210],{},[18,1466,738],{},[18,1468,1011],{}," and default-style loading work on QGIS 3.34 LTR, 3.40 LTR and QGIS 4 for GeoPackage, PostGIS, SpatiaLite, MS SQL and Oracle sources. The return value of ",[18,1471,210],{}," changed to an error string in the 3.x series; check ",[18,1474,1475],{},"if not error"," rather than truthiness of a boolean. Styles saved by newer QGIS versions may contain elements older versions ignore.",[173,1478,1480],{"id":1479},"troubleshooting","Troubleshooting",[178,1482,1483,1489,1499,1507],{},[181,1484,1485,1488],{},[397,1486,1487],{},"The style does not load automatically."," It was not saved as default, or the default belongs to a different geometry column.",[181,1490,1491,1494,1495,1498],{},[397,1492,1493],{},"Saving fails on PostGIS."," The role cannot create or write ",[18,1496,1497],{},"public.layer_styles","; grant the rights or ask an administrator to create the table.",[181,1500,1501,1504,1505,191],{},[397,1502,1503],{},"Styles seem to belong to the wrong table."," Two tables share a name in different schemas; check ",[18,1506,445],{},[181,1508,1509,1512],{},[397,1510,1511],{},"Shapefile styles cannot be saved."," Shapefiles have no style storage; save a QML or convert to GeoPackage.",[173,1514,1516],{"id":1515},"conclusion","Conclusion",[14,1518,1519,1520,1522,1523,1525],{},"Save styles into GeoPackage or PostGIS with ",[18,1521,210],{},", mark one default per table so it loads everywhere, keep alternatives as named styles, manage ",[18,1524,20],{}," with SQL when needed, switch projects between looks by loading stored styles by name, and write reviewed QML styles into deliveries so recipients see the data as intended.",[173,1527,1529],{"id":1528},"frequently-asked-questions","Frequently Asked Questions",[14,1531,1532,1535],{},[397,1533,1534],{},"Do database styles include labels and forms?","\nYes — the stored QML includes everything a QML file does, unless saved with restricted categories.",[14,1537,1538,1541],{},[397,1539,1540],{},"Can QGIS Server use database styles?","\nYes; default styles apply when the server loads the table, and named styles can be requested as WMS styles.",[14,1543,1544,1547],{},[397,1545,1546],{},"What happens if both a QML file and a database default exist?","\nThe database default is used when loading from the database; a sidecar QML is used for file-based formats.",[14,1549,1550,1553],{},[397,1551,1552],{},"Can I edit a stored style with SQL?","\nYou can update the QML text, but it is safer to load, change and save through QGIS.",[173,1555,1557],{"id":1556},"related","Related",[178,1559,1560,1565,1570,1576,1581],{},[181,1561,1562,1564],{},[26,1563,29],{"href":28}," — the guide this recipe belongs to",[181,1566,1567],{},[26,1568,1569],{"href":189},"Save and Load a QML Style in PyQGIS",[181,1571,1572],{},[26,1573,1575],{"href":1574},"\u002Fpyqgis-cartography-visualization\u002Fprogrammatic-layer-styling\u002Fcopy-style-between-layers-pyqgis\u002F","Copy a Style Between Layers in PyQGIS",[181,1577,1578],{},[26,1579,1580],{"href":580},"Use the Database Connections API in PyQGIS",[181,1582,1583],{},[26,1584,1586],{"href":1585},"\u002Fspatial-data-processing-automation\u002Fpostgis-and-database-workflows\u002Fwrite-vector-layer-to-geopackage-pyqgis\u002F","Write a Vector Layer to GeoPackage in PyQGIS",[1588,1589,1590],"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 .sjoCn, html code.shiki .sjoCn{--shiki-default:#9AA79F}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 .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}",{"title":221,"searchDepth":244,"depth":244,"links":1592},[1593,1594,1595,1596,1597,1598,1599,1600,1601,1602,1603,1604],{"id":175,"depth":244,"text":176},{"id":204,"depth":244,"text":205},{"id":406,"depth":244,"text":407},{"id":584,"depth":244,"text":585},{"id":742,"depth":244,"text":743},{"id":1030,"depth":244,"text":1031},{"id":1260,"depth":244,"text":1261},{"id":1459,"depth":244,"text":1460},{"id":1479,"depth":244,"text":1480},{"id":1515,"depth":244,"text":1516},{"id":1528,"depth":244,"text":1529},{"id":1556,"depth":244,"text":1557},"Store QGIS layer styles inside GeoPackage and PostGIS — saveStyleToDatabase and the layer_styles table, default styles that load automatically, several named styles per layer, listing and loading styles from Python, and sharing one styled dataset across a team.","md",{"slug":1608,"type":1609,"breadcrumb":1610,"datePublished":1611,"dateModified":1611},"save-styles-to-database-pyqgis","article","Save Styles to a Database","2026-10-02","\u002Fpyqgis-cartography-visualization\u002Fprogrammatic-layer-styling\u002Fsave-styles-to-database-pyqgis",{"title":5,"description":1605},"pyqgis-cartography-visualization\u002Fprogrammatic-layer-styling\u002Fsave-styles-to-database-pyqgis\u002Findex","BckgHXoUwEufts2BJjrxprFIlEn-PSvNTxngmTmEj18",1790966252915]