4

Is there a way to autoincrement the id field on a blank layer created in PyQGIS?

For example, this is a blank layer and I want to autoincrement the fid field for anything features that are added.

from qgis.core import QgsVectorLayer, QgsFeature, QgsField, QgsGeometry, QgsPointXY, QgsField, QgsProject

vl = QgsVectorLayer("Polygon", "blank_poly", "memory")
crs = vl.crs()
crs.createFromId(28355) # or whatever
vl.setCrs(crs)
pr = vl.dataProvider()
# Enter editing mode
vl.startEditing()
# add fields
pr.addAttributes( [ QgsField("fid", QVariant.Int),
                QgsField("Label",  QVariant.String,'character', 255),
                QgsField("Source", QVariant.String),
                QgsField("Date", QVariant.Date),
                QgsField("Status", QVariant.String),
                QgsField("Theme", QVariant.String),
                QgsField("SubTheme", QVariant.String),
                QgsField("Comments", QVariant.String,'character', 255),
                QgsField("Area", QVariant.String),
                QgsField("Name", QVariant.String)] )
vl.commitChanges()
# Show in project
QgsProject.instance().addMapLayer(vl)

I have attemped this: Adding feature id column that autoincrements using PyQGIS?

from qgis.core import QgsVectorLayer, QgsFeature, QgsField, QgsGeometry, QgsPointXY, QgsField, QgsProject

vl = QgsVectorLayer("Polygon", "blank_poly", "memory")
crs = vl.crs()
crs.createFromId(28355) # or whatever
vl.setCrs(crs)
pr = vl.dataProvider()
# Enter editing mode
vl.startEditing()
# add fields
pr.addAttributes( [ QgsField("fid", QVariant.Int),
                QgsField("Label",  QVariant.String,'character', 255),
                QgsField("Source", QVariant.String),
                QgsField("Date", QVariant.Date),
                QgsField("Status", QVariant.String),
                QgsField("Theme", QVariant.String),
                QgsField("SubTheme", QVariant.String),
                QgsField("Comments", QVariant.String,'character', 255),
                QgsField("Area", QVariant.String),
                QgsField("Name", QVariant.String)] )

# sequential field id updates
pr.dataProvider().addAttributes(fields)
pr.updateFields()
                
i = 0
with edit(vl):  
    for feature in pr.getFeatures():
        feature.setAttribute(feature.fieldNameIndex('fid'), i)
        i += 1                
                
vl.commitChanges()
# Show in project
QgsProject.instance().addMapLayer(vl)

But I get these errors:

Traceback (most recent call last): File "C:\PROGRA~1\QGIS32~1.7\apps\Python39\lib\code.py", line 90, in runcode exec(code, self.locals) File "", line 1, in File "", line 23, in AttributeError: 'QgsVectorDataProvider' object has no attribute 'dataProvider'

I am running on QGIS 3.22.

1 Answer 1

8

Firstly, regarding the errors you are getting. Here you are storing the layer Data Provider object in the pr variable:

pr = vl.dataProvider()

Then in these lines:

pr.dataProvider().addAttributes(fields)
pr.updateFields()

You are calling dataProvider() and updateFields() on the QgsVectorDataProvider object, however, these methods come from the QgsMapLayer and QgsVectorLayer classes, so both will cause errors.

If your goal is to update your "fid" field all at once after you have added some features, you can just do this:

with edit(vl):
    for f in vl.getFeatures():
        f['fid'] = f.id()
        # Or if you want your fid to start from zero instead of 1...
        #f['fid'] = f.id()-1
        vl.updateFeature(f)

However, you should know that this approach you are attempting will not have any any effect at the time a feature is added, it will simply update the "fid" column for already existing features.

It sounds to me like what you really want is to set a default value for your "fid" field during the layer creation using pyqgis, so that when any feature is added, the "fid" field is automatically filled with incrementing integers. In that case, you can use the following code:

vl = QgsVectorLayer("Polygon?crs=epsg:28355", "blank_poly", "memory")
pr = vl.dataProvider()
pr.addAttributes( [ QgsField("fid", QVariant.Int),
                QgsField("Label",  QVariant.String,'character', 255),
                QgsField("Source", QVariant.String),
                QgsField("Date", QVariant.Date),
                QgsField("Status", QVariant.String),
                QgsField("Theme", QVariant.String),
                QgsField("SubTheme", QVariant.String),
                QgsField("Comments", QVariant.String,'character', 255),
                QgsField("Area", QVariant.String),
                QgsField("Name", QVariant.String)] )
                
vl.updateFields()

default_val = QgsDefaultValue(
                """
                CASE
                WHEN maximum("fid") is NULL THEN 1
                WHEN "fid" is NULL THEN maximum("fid")+1
                ELSE "fid"
                END
                """,
                applyOnUpdate=True)

vl.setDefaultValueDefinition(vl.fields().lookupField('fid'), default_val)

QgsProject.instance().addMapLayer(vl)
vl.startEditing()

Again, if you want your "fid" to start from zero instead of one, change the expression to:

"""
CASE
WHEN maximum("fid") is NULL THEN 0
WHEN "fid" is NULL THEN maximum("fid")+1
ELSE "fid"
END
"""
5
  • Great. Thanks for the detailed explanation. Commented Sep 7, 2022 at 3:08
  • 1
    @william3031, no worries :-)
    – Ben W
    Commented Sep 7, 2022 at 3:10
  • Also, I note that there was no need to import any of the functions, is that just because I was running this from python within QGIS? Commented Sep 7, 2022 at 3:10
  • I assume it relates to them being imported here: docs.qgis.org/2.8/en/docs/pyqgis_developer_cookbook/… Commented Sep 7, 2022 at 3:13
  • 1
    @william3031, yes- correct. When running code from the QGIS Python console, you don't need to explicitly import qgis.core modules, qt modules or built in Python modules. You still need to import any installed 3rd party Python modules. However in plugins, custom functions, Python actions etc. you must explicitly import all used modules/ classes.
    – Ben W
    Commented Sep 7, 2022 at 3:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.