From cb175d548a5b577ec31e8e99cdae016634ec1253 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 7 Oct 2024 14:19:12 +0200 Subject: [PATCH] Fix style constraint save when strength is NotSet Fix #58341 I feel that the underlying issue remains (which is that setting the strength to NotSet removes it from the field but not from the layer and that the default strength is Hard) but since it was clearly made in purpose 8 years ago I am not keen to change it lightly. --- src/core/vector/qgsvectorlayer.cpp | 51 +++++++++++++++++++++++-- tests/src/python/test_qgsvectorlayer.py | 29 ++++++++++++++ 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/src/core/vector/qgsvectorlayer.cpp b/src/core/vector/qgsvectorlayer.cpp index 7810f5273e64..eaa62e0edce1 100644 --- a/src/core/vector/qgsvectorlayer.cpp +++ b/src/core/vector/qgsvectorlayer.cpp @@ -3141,10 +3141,53 @@ bool QgsVectorLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString { QDomElement constraintElem = doc.createElement( QStringLiteral( "constraint" ) ); constraintElem.setAttribute( QStringLiteral( "field" ), field.name() ); - constraintElem.setAttribute( QStringLiteral( "constraints" ), field.constraints().constraints() ); - constraintElem.setAttribute( QStringLiteral( "unique_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintUnique ) ); - constraintElem.setAttribute( QStringLiteral( "notnull_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintNotNull ) ); - constraintElem.setAttribute( QStringLiteral( "exp_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintExpression ) ); + + // This manipulation is required because when the strength is set to NotSet the constraint is removed from + // the field but when the field is queried for the strength of a not-existing constraint it returns Hard by default, + // see issue #58431 + QgsFieldConstraints::Constraints constraints { mFieldConstraints.value( field.name() ) }; + QgsFieldConstraints::ConstraintStrength uniqueStrength { QgsFieldConstraints::ConstraintStrength::ConstraintStrengthNotSet }; + QgsFieldConstraints::ConstraintStrength notNullStrength { QgsFieldConstraints::ConstraintStrength::ConstraintStrengthNotSet }; + QgsFieldConstraints::ConstraintStrength expressionStrength { QgsFieldConstraints::ConstraintStrength::ConstraintStrengthNotSet }; + + QPair strengthKey { qMakePair( field.name(), QgsFieldConstraints::ConstraintUnique ) }; + + if ( mFieldConstraintStrength.contains( strengthKey ) && mFieldConstraintStrength.value( strengthKey ) == QgsFieldConstraints::ConstraintStrength::ConstraintStrengthNotSet ) + { + constraints.setFlag( QgsFieldConstraints::Constraint::ConstraintUnique, false ); + } + else + { + uniqueStrength = mFieldConstraintStrength.value( strengthKey ); + } + + strengthKey = qMakePair( field.name(), QgsFieldConstraints::ConstraintNotNull ); + + if ( mFieldConstraintStrength.contains( strengthKey ) && mFieldConstraintStrength.value( strengthKey ) == QgsFieldConstraints::ConstraintStrength::ConstraintStrengthNotSet ) + { + constraints.setFlag( QgsFieldConstraints::Constraint::ConstraintNotNull, false ); + } + else + { + notNullStrength = mFieldConstraintStrength.value( strengthKey ); + } + + strengthKey = qMakePair( field.name(), QgsFieldConstraints::ConstraintExpression ); + + if ( mFieldConstraintStrength.contains( strengthKey ) && mFieldConstraintStrength.value( strengthKey ) == QgsFieldConstraints::ConstraintStrength::ConstraintStrengthNotSet ) + { + constraints.setFlag( QgsFieldConstraints::Constraint::ConstraintExpression, false ); + } + else + { + expressionStrength = mFieldConstraintStrength.value( strengthKey ); + } + + + constraintElem.setAttribute( QStringLiteral( "constraints" ), constraints ); + constraintElem.setAttribute( QStringLiteral( "unique_strength" ), uniqueStrength ); + constraintElem.setAttribute( QStringLiteral( "notnull_strength" ), notNullStrength ); + constraintElem.setAttribute( QStringLiteral( "exp_strength" ), expressionStrength ); constraintsElem.appendChild( constraintElem ); } node.appendChild( constraintsElem ); diff --git a/tests/src/python/test_qgsvectorlayer.py b/tests/src/python/test_qgsvectorlayer.py index 881578a0bb0f..14d6c3efc260 100644 --- a/tests/src/python/test_qgsvectorlayer.py +++ b/tests/src/python/test_qgsvectorlayer.py @@ -4675,6 +4675,35 @@ def test_selection_properties(self): self.assertEqual(vl2.selectionProperties().selectionColor(), QColor(255, 0, 0)) + def testConstraintsStrengthNotSet(self): + """Test issue GH #58431 when strength NotSet becomes Hard""" + + # Create a memory layer with unique constraints + layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer", "test_unique", "memory") + self.assertTrue(layer.isValid()) + + # Se not constraint on fldtxt + layer.setFieldConstraint(0, QgsFieldConstraints.Constraint.ConstraintNotNull, QgsFieldConstraints.ConstraintStrength.ConstraintStrengthSoft) + layer.setFieldConstraint(0, QgsFieldConstraints.Constraint.ConstraintUnique, QgsFieldConstraints.ConstraintStrength.ConstraintStrengthNotSet) + + # Export the style to QML + style = QgsMapLayerStyle() + temp_file = tempfile.mktemp(suffix='.qml') + layer.saveNamedStyle(temp_file) + layer.loadNamedStyle(temp_file) + + # Check constraints at the field level + field = layer.fields().at(0) + constraints = field.constraints() + self.assertEqual(constraints.constraintStrength(QgsFieldConstraints.Constraint.ConstraintNotNull), QgsFieldConstraints.ConstraintStrength.ConstraintStrengthSoft) + self.assertEqual(constraints.constraintStrength(QgsFieldConstraints.Constraint.ConstraintUnique), QgsFieldConstraints.ConstraintStrength.ConstraintStrengthNotSet) + + # Check constraints at the layer level + constraints = layer.fieldConstraintsAndStrength(0) + self.assertEqual(constraints[QgsFieldConstraints.Constraint.ConstraintNotNull], QgsFieldConstraints.ConstraintStrength.ConstraintStrengthSoft) + self.assertEqual(constraints[QgsFieldConstraints.Constraint.ConstraintUnique], QgsFieldConstraints.ConstraintStrength.ConstraintStrengthNotSet) + + # TODO: # - fetch rect: feat with changed geometry: 1. in rect, 2. out of rect # - more join tests