From 606bd36e1415abfeb058123396b9ea3ee43a7e2d Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Thu, 22 Oct 2026 16:07:09 +0700 Subject: [PATCH 1/4] Support vector layer virtual field value updates within the feature form --- src/gui/qfattributeformmodelbase.cpp | 48 ++++++++++++++++++++-------- src/gui/qfattributeformmodelbase.h | 7 ++-- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/gui/qfattributeformmodelbase.cpp b/src/gui/qfattributeformmodelbase.cpp index 7fb838bbb72..63c4e3cdee2 100644 --- a/src/gui/qfattributeformmodelbase.cpp +++ b/src/gui/qfattributeformmodelbase.cpp @@ -149,7 +149,7 @@ bool QfAttributeFormModelBase::setData( const QModelIndex &index, const QVariant mExpressionContext << QgsExpressionContextUtils::formScope( mFeatureModel->feature() ); synchronizeFieldValue( fieldIndex, value ); } - updateDefaultValues( fieldIndex ); + updateDefaultValuesAndVirtualFields( fieldIndex ); updateVisibilityAndConstraints( fieldIndex ); return changed; } @@ -364,6 +364,8 @@ void QfAttributeFormModelBase::applyRelationshipDefaultValues() } } } + + updateEditorWidgetCodes( QString(), true ); } void QfAttributeFormModelBase::activateAllRememberValues() @@ -782,7 +784,7 @@ void QfAttributeFormModelBase::synchronizeFieldValue( int fieldIndex, QVariant v } } -void QfAttributeFormModelBase::updateDefaultValues( int fieldIndex, QVector updatedFields ) +void QfAttributeFormModelBase::updateDefaultValuesAndVirtualFields( int fieldIndex, QVector updatedFields ) { const QgsFields fields = mFeatureModel->feature().fields(); if ( fieldIndex < 0 || fieldIndex >= fields.size() ) @@ -796,10 +798,26 @@ void QfAttributeFormModelBase::updateDefaultValues( int fieldIndex, QVector for ( ; fieldIterator != mFields.constEnd(); ++fieldIterator ) { const int fidx = fieldIterator.value(); - if ( fidx == fieldIndex || !fields.at( fidx ).defaultValueDefinition().isValid() || !fields.at( fidx ).defaultValueDefinition().applyOnUpdate() ) + if ( fidx == fieldIndex ) + { continue; + } + + const QgsField field = fields.at( fidx ); + QgsExpression exp; + if ( fields.fieldOrigin( fidx ) == Qgis::FieldOrigin::Expression ) + { + exp.setExpression( mLayer->expressionField( fidx ) ); + } + else + { + if ( !field.defaultValueDefinition().isValid() || !field.defaultValueDefinition().applyOnUpdate() ) + { + continue; + } - QgsExpression exp( fields.at( fidx ).defaultValueDefinition().expression() ); + exp.setExpression( field.defaultValueDefinition().expression() ); + } exp.prepare( &mExpressionContext ); // avoid cost of value update if expression doesn't contain the field which triggered the default values update @@ -808,9 +826,9 @@ void QfAttributeFormModelBase::updateDefaultValues( int fieldIndex, QVector continue; } - const QVariant defaultValue = exp.evaluate( &mExpressionContext ); + const QVariant value = exp.evaluate( &mExpressionContext ); const QVariant previousValue = mFeatureModel->data( mFeatureModel->index( fidx ), QfFeatureModel::AttributeValue ); - const bool success = mFeatureModel->setData( mFeatureModel->index( fidx ), defaultValue, QfFeatureModel::AttributeValue ); + const bool success = mFeatureModel->setData( mFeatureModel->index( fidx ), value, QfFeatureModel::AttributeValue ); const QVariant updatedValue = mFeatureModel->data( mFeatureModel->index( fidx ), QfFeatureModel::AttributeValue ); if ( success && updatedValue != previousValue ) { @@ -821,7 +839,7 @@ void QfAttributeFormModelBase::updateDefaultValues( int fieldIndex, QVector if ( !updatedFields.contains( fidx ) ) { updatedFields.append( fidx ); - updateDefaultValues( fidx, updatedFields ); + updateDefaultValuesAndVirtualFields( fidx, updatedFields ); } updateVisibilityAndConstraints( fidx ); } @@ -831,7 +849,7 @@ void QfAttributeFormModelBase::updateDefaultValues( int fieldIndex, QVector updateEditorWidgetCodes( fieldName ); } -bool QfAttributeFormModelBase::codeRequiresUpdate( const QString &fieldName, const QString &code, const QRegularExpression ®Ex ) +bool QfAttributeFormModelBase::codeRequiresUpdate( const QString &fieldName, bool updateRelationships, const QString &code, const QRegularExpression ®Ex ) { if ( !mEditorWidgetCodesRequirements.contains( code ) ) { @@ -847,11 +865,13 @@ bool QfAttributeFormModelBase::codeRequiresUpdate( const QString &fieldName, con exp.prepare( &mExpressionContext ); codeRequirements.referencedColumns.unite( exp.referencedColumns() ); codeRequirements.formScope = codeRequirements.formScope || QgsValueRelationFieldFormatter::expressionRequiresFormScope( expression ); + qDebug() << "---" << exp.expression() << ( exp.referencedFunctions().contains( QStringLiteral( "relation_aggregate" ) ) ? "contains rel agg." : "nope" ); + codeRequirements.relationships = exp.referencedFunctions().contains( QStringLiteral( "relation_aggregate" ) ); } mEditorWidgetCodesRequirements.insert( code, codeRequirements ); } - return mEditorWidgetCodesRequirements[code].referencedColumns.contains( fieldName ) || mEditorWidgetCodesRequirements[code].referencedColumns.contains( QgsFeatureRequest::ALL_ATTRIBUTES ) || mEditorWidgetCodesRequirements[code].formScope; + return mEditorWidgetCodesRequirements[code].referencedColumns.contains( fieldName ) || mEditorWidgetCodesRequirements[code].referencedColumns.contains( QgsFeatureRequest::ALL_ATTRIBUTES ) || mEditorWidgetCodesRequirements[code].formScope || ( updateRelationships && mEditorWidgetCodesRequirements[code].relationships ); } void QfAttributeFormModelBase::updateDataDefinedProperties( const QString &fieldName ) @@ -901,7 +921,7 @@ void QfAttributeFormModelBase::updateDataDefinedProperties( const QString &field } } -void QfAttributeFormModelBase::updateEditorWidgetCodes( const QString &fieldName ) +void QfAttributeFormModelBase::updateEditorWidgetCodes( const QString &fieldName, bool updateRelationships ) { QMap::ConstIterator editorWidgetCodesIterator( mEditorWidgetCodes.constBegin() ); for ( ; editorWidgetCodesIterator != mEditorWidgetCodes.constEnd(); editorWidgetCodesIterator++ ) @@ -915,7 +935,7 @@ void QfAttributeFormModelBase::updateEditorWidgetCodes( const QString &fieldName if ( item->data( QfAttributeFormModel::ElementType ) == QStringLiteral( "qml" ) || item->data( QfAttributeFormModel::ElementType ) == QStringLiteral( "html" ) ) { const thread_local QRegularExpression sRegEx( "expression\\.evaluate\\(\\s*\\\"(.*?[^\\\\])\\\"\\s*\\)", QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption ); - if ( codeRequiresUpdate( fieldName, code, sRegEx ) ) + if ( codeRequiresUpdate( fieldName, updateRelationships, code, sRegEx ) ) { QRegularExpressionMatch match = sRegEx.match( code ); while ( match.hasMatch() ) @@ -953,7 +973,7 @@ void QfAttributeFormModelBase::updateEditorWidgetCodes( const QString &fieldName else if ( item->data( QfAttributeFormModel::ElementType ) == QStringLiteral( "text" ) ) { const thread_local QRegularExpression sRegEx( QStringLiteral( "\\[%(.*?)%\\]" ), QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption ); - if ( codeRequiresUpdate( fieldName, code, sRegEx ) ) + if ( codeRequiresUpdate( fieldName, updateRelationships, code, sRegEx ) ) { code = QgsExpression::replaceExpressionText( code, &mExpressionContext ); item->setData( code, QfAttributeFormModel::EditorWidgetCode ); @@ -1175,7 +1195,7 @@ bool QfAttributeFormModelBase::changeAttribute( const QString &name, const QVari mExpressionContext.popScope(); mExpressionContext << QgsExpressionContextUtils::formScope( mFeatureModel->feature() ); synchronizeFieldValue( fieldIndex, value ); - updateDefaultValues( fieldIndex ); + updateDefaultValuesAndVirtualFields( fieldIndex ); updateVisibilityAndConstraints( fieldIndex ); } @@ -1192,7 +1212,7 @@ bool QfAttributeFormModelBase::changeGeometry( const QgsGeometry &geometry ) { mExpressionContext.popScope(); mExpressionContext << QgsExpressionContextUtils::formScope( mFeatureModel->feature() ); - updateDefaultValues(); + updateDefaultValuesAndVirtualFields(); updateVisibilityAndConstraints(); } diff --git a/src/gui/qfattributeformmodelbase.h b/src/gui/qfattributeformmodelbase.h index a47ef13ab1c..1f0a50d86d5 100644 --- a/src/gui/qfattributeformmodelbase.h +++ b/src/gui/qfattributeformmodelbase.h @@ -107,6 +107,7 @@ class QfAttributeFormModelBase : public QStandardItemModel { QSet referencedColumns; bool formScope = false; + bool relationships = false; }; /** @@ -129,16 +130,16 @@ class QfAttributeFormModelBase : public QStandardItemModel void synchronizeFieldValue( int fieldIndex, QVariant value ); //! Update default values refering to the \a fieldIndex. - void updateDefaultValues( int fieldIndex = -1, QVector updatedFields = QVector() ); + void updateDefaultValuesAndVirtualFields( int fieldIndex = -1, QVector updatedFields = QVector() ); //! Update QML, HTML, and text widget code. - void updateEditorWidgetCodes( const QString &fieldName ); + void updateEditorWidgetCodes( const QString &fieldName, bool updateRelationships = false ); //! Update expression-driven alias and read-only values. void updateDataDefinedProperties( const QString &fieldName ); //! Check if the given \a code requires update. - bool codeRequiresUpdate( const QString &fieldName, const QString &code, const QRegularExpression ®Ex ); + bool codeRequiresUpdate( const QString &fieldName, bool updateRelationships, const QString &code, const QRegularExpression ®Ex ); //! Udate the visibility state of groups as well as constraints of field items void updateVisibilityAndConstraints( int fieldIndex = -1 ); From 6615070300377a025ffed20ff58a4a0746560b29 Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Thu, 22 Oct 2026 16:40:26 +0700 Subject: [PATCH 2/4] Insure that every expression-driven parts of the feature form (constraints, visibility, etc.) take into account relationship changes --- src/gui/qfattributeformmodelbase.cpp | 33 +++++++++++++++++----------- src/gui/qfattributeformmodelbase.h | 10 ++++----- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/gui/qfattributeformmodelbase.cpp b/src/gui/qfattributeformmodelbase.cpp index 63c4e3cdee2..dcb778ba104 100644 --- a/src/gui/qfattributeformmodelbase.cpp +++ b/src/gui/qfattributeformmodelbase.cpp @@ -365,7 +365,9 @@ void QfAttributeFormModelBase::applyRelationshipDefaultValues() } } + updateDefaultValuesAndVirtualFields( -1, true ); updateEditorWidgetCodes( QString(), true ); + updateVisibilityAndConstraints( -1, true ); } void QfAttributeFormModelBase::activateAllRememberValues() @@ -784,12 +786,14 @@ void QfAttributeFormModelBase::synchronizeFieldValue( int fieldIndex, QVariant v } } -void QfAttributeFormModelBase::updateDefaultValuesAndVirtualFields( int fieldIndex, QVector updatedFields ) +void QfAttributeFormModelBase::updateDefaultValuesAndVirtualFields( int fieldIndex, bool updateRelationships, QVector updatedFields ) { const QgsFields fields = mFeatureModel->feature().fields(); - if ( fieldIndex < 0 || fieldIndex >= fields.size() ) + if ( fieldIndex < -1 || fieldIndex >= fields.size() ) + { return; - const QString fieldName = fields.at( fieldIndex ).name(); + } + const QString fieldName = fieldIndex > -1 ? fields.at( fieldIndex ).name() : QString(); mExpressionContext.setFields( fields ); mExpressionContext.setFeature( mFeatureModel->feature() ); @@ -821,7 +825,7 @@ void QfAttributeFormModelBase::updateDefaultValuesAndVirtualFields( int fieldInd exp.prepare( &mExpressionContext ); // avoid cost of value update if expression doesn't contain the field which triggered the default values update - if ( !exp.referencedColumns().contains( fieldName ) && !exp.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) ) + if ( !exp.referencedColumns().contains( fieldName ) && !exp.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) && ( !updateRelationships || !exp.referencedFunctions().contains( QStringLiteral( "relation_aggregate" ) ) ) ) { continue; } @@ -839,14 +843,17 @@ void QfAttributeFormModelBase::updateDefaultValuesAndVirtualFields( int fieldInd if ( !updatedFields.contains( fidx ) ) { updatedFields.append( fidx ); - updateDefaultValuesAndVirtualFields( fidx, updatedFields ); + updateDefaultValuesAndVirtualFields( fidx, false, updatedFields ); } updateVisibilityAndConstraints( fidx ); } } - updateDataDefinedProperties( fieldName ); - updateEditorWidgetCodes( fieldName ); + if ( !fieldName.isEmpty() ) + { + updateDataDefinedProperties( fieldName ); + updateEditorWidgetCodes( fieldName ); + } } bool QfAttributeFormModelBase::codeRequiresUpdate( const QString &fieldName, bool updateRelationships, const QString &code, const QRegularExpression ®Ex ) @@ -874,7 +881,7 @@ bool QfAttributeFormModelBase::codeRequiresUpdate( const QString &fieldName, boo return mEditorWidgetCodesRequirements[code].referencedColumns.contains( fieldName ) || mEditorWidgetCodesRequirements[code].referencedColumns.contains( QgsFeatureRequest::ALL_ATTRIBUTES ) || mEditorWidgetCodesRequirements[code].formScope || ( updateRelationships && mEditorWidgetCodesRequirements[code].relationships ); } -void QfAttributeFormModelBase::updateDataDefinedProperties( const QString &fieldName ) +void QfAttributeFormModelBase::updateDataDefinedProperties( const QString &fieldName, bool updateRelationships ) { QMap::ConstIterator aliasExpressionsIterator( mAliasExpressions.constBegin() ); for ( ; aliasExpressionsIterator != mAliasExpressions.constEnd(); aliasExpressionsIterator++ ) @@ -886,7 +893,7 @@ void QfAttributeFormModelBase::updateDataDefinedProperties( const QString &field } QgsExpression exp( aliasExpressionsIterator.value() ); - exp.referencedColumns().contains( fieldName ); + if ( exp.referencedColumns().contains( fieldName ) || exp.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) || ( updateRelationships && exp.referencedFunctions().contains( "relation_aggregate" ) ) ) { exp.prepare( &mExpressionContext ); QVariant result = exp.evaluate( &mExpressionContext ); @@ -912,7 +919,7 @@ void QfAttributeFormModelBase::updateDataDefinedProperties( const QString &field } QgsExpression exp( readOnlyExpressionsIterator.value() ); - exp.referencedColumns().contains( fieldName ); + if ( exp.referencedColumns().contains( fieldName ) || exp.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) || ( updateRelationships && exp.referencedFunctions().contains( "relation_aggregate" ) ) ) { exp.prepare( &mExpressionContext ); QVariant result = exp.evaluate( &mExpressionContext ); @@ -1009,7 +1016,7 @@ void _checkChildrenValidity( const QStandardItem *parent, bool &hardValidity, bo } }; -void QfAttributeFormModelBase::updateVisibilityAndConstraints( int fieldIndex ) +void QfAttributeFormModelBase::updateVisibilityAndConstraints( int fieldIndex, bool updateRelationships ) { const QString fieldName = fieldIndex > -1 && fieldIndex < mLayer->fields().size() ? mLayer->fields().at( fieldIndex ).name() : QString(); QgsFields fields = mFeatureModel->feature().fields(); @@ -1021,7 +1028,7 @@ void QfAttributeFormModelBase::updateVisibilityAndConstraints( int fieldIndex ) { // If triggered by an updated field index (fieldIndex), check if the visibility // expression refers to that field - if ( fieldIndex == -1 || it.first.referencedColumns().contains( fieldName ) || it.first.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) ) + if ( fieldIndex == -1 || it.first.referencedColumns().contains( fieldName ) || it.first.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) || ( updateRelationships && it.first.referencedFunctions().contains( QStringLiteral( "relation_aggregate" ) ) ) ) { QgsExpression exp = it.first; exp.prepare( &mExpressionContext ); @@ -1049,7 +1056,7 @@ void QfAttributeFormModelBase::updateVisibilityAndConstraints( int fieldIndex ) // Check whether the current field iterator index (fidx) has an expression constraints referencing the // updated field index (fieldIndex) which triggered a constraints update const QgsExpression expression = mLayer->fields().at( fidx ).constraints().constraintExpression(); - if ( !expression.referencedColumns().contains( fieldName ) && !expression.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) ) + if ( !expression.referencedColumns().contains( fieldName ) && !expression.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) && ( !updateRelationships || !expression.referencedFunctions().contains( QStringLiteral( "relation_aggregate" ) ) ) ) { continue; } diff --git a/src/gui/qfattributeformmodelbase.h b/src/gui/qfattributeformmodelbase.h index 1f0a50d86d5..9b4d371b2e4 100644 --- a/src/gui/qfattributeformmodelbase.h +++ b/src/gui/qfattributeformmodelbase.h @@ -130,20 +130,20 @@ class QfAttributeFormModelBase : public QStandardItemModel void synchronizeFieldValue( int fieldIndex, QVariant value ); //! Update default values refering to the \a fieldIndex. - void updateDefaultValuesAndVirtualFields( int fieldIndex = -1, QVector updatedFields = QVector() ); + void updateDefaultValuesAndVirtualFields( int fieldIndex = -1, bool updateRelationships = false, QVector updatedFields = QVector() ); //! Update QML, HTML, and text widget code. void updateEditorWidgetCodes( const QString &fieldName, bool updateRelationships = false ); //! Update expression-driven alias and read-only values. - void updateDataDefinedProperties( const QString &fieldName ); + void updateDataDefinedProperties( const QString &fieldName, bool updateRelationships = false ); + + //! Udate the visibility state of groups as well as constraints of field items + void updateVisibilityAndConstraints( int fieldIndex = -1, bool updateRelationships = false ); //! Check if the given \a code requires update. bool codeRequiresUpdate( const QString &fieldName, bool updateRelationships, const QString &code, const QRegularExpression ®Ex ); - //! Udate the visibility state of groups as well as constraints of field items - void updateVisibilityAndConstraints( int fieldIndex = -1 ); - void setConstraintsHardValid( bool constraintsHardValid ); void setConstraintsSoftValid( bool constraintsSoftValid ); From 24dca7358f2ae4f691255817eb769de59925d53e Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Thu, 22 Oct 2026 17:08:53 +0700 Subject: [PATCH 3/4] Add test coverage for virtual field updates within the feature form --- test/test_attributeformmodel.cpp | 34 +++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/test/test_attributeformmodel.cpp b/test/test_attributeformmodel.cpp index 3eb0d75ffcf..cc0ae66ea5e 100644 --- a/test/test_attributeformmodel.cpp +++ b/test/test_attributeformmodel.cpp @@ -40,7 +40,6 @@ TEST_CASE( "AttributeFormModel" ) QgsFeature f1; f1.setAttributes( QgsAttributes() << 1 << QStringLiteral( "string_a1" ) << QStringLiteral( "string_b1" ) ); - qDebug() << f1.attributes(); QgsFeature f2; f2.setAttributes( QgsAttributes() << 2 << QStringLiteral( "string_a2" ) << QStringLiteral( "string_b2" ) ); QgsFeature f3; @@ -101,6 +100,39 @@ TEST_CASE( "AttributeFormModel" ) REQUIRE( feature.attributes().at( 2 ) == QStringLiteral( "edit_feature__" ) ); } + SECTION( "VirtualFieldUpdate" ) + { + std::unique_ptr layer2 = std::make_unique( QStringLiteral( "Point?crs=EPSG:3857&field=fid:integer&field=str:string&field=str2:string" ), QStringLiteral( "Input Layer" ), QStringLiteral( "memory" ) ); + REQUIRE( layer2->isValid() ); + + QgsFeature l2f1; + l2f1.setAttributes( QgsAttributes() << 1 << QStringLiteral( "string_a1" ) << QStringLiteral( "string_b1" ) ); + + layer2->startEditing(); + layer2->addFeature( l2f1 ); + layer2->commitChanges(); + REQUIRE( layer2->featureCount() == 1 ); + + layer2->addExpressionField( QStringLiteral( "\"str\"" ), QgsField( QStringLiteral( "str_virtual" ), QMetaType::Type::QString ) ); + + // Trigger attribute form re-build + featureModel->setCurrentLayer( layer2.get() ); + + featureModel->setFeature( layer2->getFeature( 1 ) ); + REQUIRE( attributeFormModel->attribute( QStringLiteral( "str" ) ) == QStringLiteral( "string_a1" ) ); + REQUIRE( attributeFormModel->attribute( QStringLiteral( "str_virtual" ) ) == QStringLiteral( "string_a1" ) ); + REQUIRE( attributeFormModel->data( attributeFormModel->index( 1, 0 ), QfAttributeFormModel::AttributeValue ) == QStringLiteral( "string_a1" ) ); + REQUIRE( attributeFormModel->data( attributeFormModel->index( 3, 0 ), QfAttributeFormModel::AttributeValue ) == QStringLiteral( "string_a1" ) ); + + attributeFormModel->changeAttribute( QStringLiteral( "str" ), QStringLiteral( "string_a1_modified" ) ); + REQUIRE( attributeFormModel->attribute( QStringLiteral( "str" ) ) == QStringLiteral( "string_a1_modified" ) ); + REQUIRE( attributeFormModel->attribute( QStringLiteral( "str_virtual" ) ) == QStringLiteral( "string_a1_modified" ) ); + REQUIRE( attributeFormModel->data( attributeFormModel->index( 1, 0 ), QfAttributeFormModel::AttributeValue ) == QStringLiteral( "string_a1_modified" ) ); + REQUIRE( attributeFormModel->data( attributeFormModel->index( 3, 0 ), QfAttributeFormModel::AttributeValue ) == QStringLiteral( "string_a1_modified" ) ); + + featureModel->setCurrentLayer( layer.get() ); + } + SECTION( "ReadOnlyDataDefinedProperty" ) { featureModel->resetFeature(); From 7465eae50f57a105adb1048d42a70e96181f9975 Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Sat, 24 Oct 2026 15:22:05 +0700 Subject: [PATCH 4/4] Address review --- src/gui/qfattributeformmodelbase.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gui/qfattributeformmodelbase.cpp b/src/gui/qfattributeformmodelbase.cpp index dcb778ba104..e86b8126ec1 100644 --- a/src/gui/qfattributeformmodelbase.cpp +++ b/src/gui/qfattributeformmodelbase.cpp @@ -872,8 +872,7 @@ bool QfAttributeFormModelBase::codeRequiresUpdate( const QString &fieldName, boo exp.prepare( &mExpressionContext ); codeRequirements.referencedColumns.unite( exp.referencedColumns() ); codeRequirements.formScope = codeRequirements.formScope || QgsValueRelationFieldFormatter::expressionRequiresFormScope( expression ); - qDebug() << "---" << exp.expression() << ( exp.referencedFunctions().contains( QStringLiteral( "relation_aggregate" ) ) ? "contains rel agg." : "nope" ); - codeRequirements.relationships = exp.referencedFunctions().contains( QStringLiteral( "relation_aggregate" ) ); + codeRequirements.relationships = codeRequirements.relationships || exp.referencedFunctions().contains( QStringLiteral( "relation_aggregate" ) ); } mEditorWidgetCodesRequirements.insert( code, codeRequirements ); }