From bf611b2a18fc33ed0f358fb20599a5bb8c72d009 Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Fri, 27 Mar 2026 17:18:36 +0200 Subject: [PATCH 1/6] Added old logic to handle displayed foreign key and update the UI and rawValue Kept the new logic is the field is not displayed --- app/attributes/attributecontroller.cpp | 30 +++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/app/attributes/attributecontroller.cpp b/app/attributes/attributecontroller.cpp index af1822374..d1662c21d 100644 --- a/app/attributes/attributecontroller.cpp +++ b/app/attributes/attributecontroller.cpp @@ -129,13 +129,33 @@ void AttributeController::prefillRelationReferenceField() { QVariant foreignKey = mParentController->featureLayerPair().feature().attribute( fieldPair.referencedField() ); QString referencingField = fieldPair.referencingField(); - const QgsVectorLayer *childLayer = mLinkedRelation.referencingLayer(); - if ( childLayer ) + + // set the foreign key via FormItem, so that the UI and validation are updated + bool setViaFormItem = false; + QMap>::iterator formItemsIterator = mFormItems.begin(); + while ( formItemsIterator != mFormItems.end() ) + { + std::shared_ptr itemData = formItemsIterator.value(); + if ( itemData->field().name() == referencingField ) + { + setFormValue( itemData->id(), foreignKey ); + setViaFormItem = true; + break; + } + ++formItemsIterator; + } + + // if the field is not in the displayed in the form, set the attribute directly on the feature + if ( !setViaFormItem ) { - const int fieldIndex = childLayer->fields().lookupField( referencingField ); - if ( fieldIndex != -1 ) + const QgsVectorLayer *childLayer = mLinkedRelation.referencingLayer(); + if ( childLayer ) { - mFeatureLayerPair.featureRef().setAttribute( fieldIndex, foreignKey ); + const int fieldIndex = childLayer->fields().lookupField( referencingField ); + if ( fieldIndex != -1 ) + { + mFeatureLayerPair.featureRef().setAttribute( fieldIndex, foreignKey ); + } } } } From b4eb9bda737653bc0a29ea9af43a3bd17c996362 Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Mon, 30 Mar 2026 13:00:48 +0300 Subject: [PATCH 2/6] Simplified logic --- app/attributes/attributecontroller.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/app/attributes/attributecontroller.cpp b/app/attributes/attributecontroller.cpp index d1662c21d..1b857a2c6 100644 --- a/app/attributes/attributecontroller.cpp +++ b/app/attributes/attributecontroller.cpp @@ -130,32 +130,30 @@ void AttributeController::prefillRelationReferenceField() QVariant foreignKey = mParentController->featureLayerPair().feature().attribute( fieldPair.referencedField() ); QString referencingField = fieldPair.referencingField(); - // set the foreign key via FormItem, so that the UI and validation are updated - bool setViaFormItem = false; - QMap>::iterator formItemsIterator = mFormItems.begin(); - while ( formItemsIterator != mFormItems.end() ) + std::shared_ptr formItem; + for ( const auto &item : mFormItems ) { - std::shared_ptr itemData = formItemsIterator.value(); - if ( itemData->field().name() == referencingField ) + if ( item->field().name() == referencingField ) { - setFormValue( itemData->id(), foreignKey ); - setViaFormItem = true; + formItem = item; break; } - ++formItemsIterator; } - // if the field is not in the displayed in the form, set the attribute directly on the feature - if ( !setViaFormItem ) + if ( formItem ) { + //if the field is in the form, setFormValue updates both the feature attribute and UI + setFormValue( formItem->id(), foreignKey ); + } + else + { + // if thefield is not displayed in the form, then set the attribute directly on the feature const QgsVectorLayer *childLayer = mLinkedRelation.referencingLayer(); if ( childLayer ) { const int fieldIndex = childLayer->fields().lookupField( referencingField ); if ( fieldIndex != -1 ) - { mFeatureLayerPair.featureRef().setAttribute( fieldIndex, foreignKey ); - } } } } From 7107f71c561e3cb6d92b11c2e848976cba19054f Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Mon, 30 Mar 2026 13:12:42 +0300 Subject: [PATCH 3/6] Added unit test --- app/test/testattributecontroller.cpp | 53 ++++++++++++++++++++++++++++ app/test/testattributecontroller.h | 6 ++++ 2 files changed, 59 insertions(+) diff --git a/app/test/testattributecontroller.cpp b/app/test/testattributecontroller.cpp index 9843a9af3..e1041a588 100644 --- a/app/test/testattributecontroller.cpp +++ b/app/test/testattributecontroller.cpp @@ -1219,3 +1219,56 @@ void TestAttributeController::testPhotoSketchingSave() QCOMPARE( f.attribute( testCaseResult.fieldIdx ), testCaseResult.expectedNewFieldValue ); } } + +void TestAttributeController::testPrefillRelationReferenceField() +{ + QString projectDir = TestUtils::testDataDir() + "/planes"; + QVERIFY( QgsProject::instance()->read( projectDir + "/quickapp_project.qgs" ) ); + + QgsVectorLayer *airportsLayer = static_cast( + QgsProject::instance()->mapLayersByName( QStringLiteral( "airports" ) ).at( 0 ) ); + QVERIFY( airportsLayer && airportsLayer->isValid() ); + + QgsVectorLayer *towersLayer = static_cast( + QgsProject::instance()->mapLayersByName( QStringLiteral( "airport-towers" ) ).at( 0 ) ); + QVERIFY( towersLayer && towersLayer->isValid() ); + + QgsRelation relation = QgsProject::instance()->relationManager()->relation( + QStringLiteral( "airport_to_airport_fk_airports_3_fid" ) ); + QVERIFY( relation.isValid() ); + + // parent controller holds an existing airports feature + QgsFeature parentFeature = airportsLayer->getFeature( 1 ); + QVERIFY( parentFeature.isValid() ); + + AttributeController parentController; + parentController.setFeatureLayerPair( FeatureLayerPair( parentFeature, airportsLayer ) ); + + // child controller holds a new empty airport-towers feature + QgsFeature childFeature; + childFeature.setValid( true ); + childFeature.setFields( towersLayer->fields(), true ); + + AttributeController childController; + childController.setFeatureLayerPair( FeatureLayerPair( childFeature, towersLayer ) ); + childController.setLinkedRelation( relation ); + childController.setParentController( &parentController ); // triggers prefillRelationReferenceField + + // find the airport_fk FormItem in the child controller + const FormItem *fkItem = nullptr; + const TabItem *tab = childController.tabItem( 0 ); + QVERIFY( tab ); + for ( const QUuid &id : tab->formItems() ) + { + const FormItem *item = childController.formItem( id ); + if ( item && item->field().name() == QLatin1String( "airport_fk" ) ) + { + fkItem = item; + break; + } + } + + // compare that the rawValue is updated after setting the linked relation + QVERIFY( fkItem ); + QCOMPARE( fkItem->rawValue(), parentFeature.attribute( QStringLiteral( "fid" ) ) ); +} diff --git a/app/test/testattributecontroller.h b/app/test/testattributecontroller.h index 8090d00ff..5fa25b9dc 100644 --- a/app/test/testattributecontroller.h +++ b/app/test/testattributecontroller.h @@ -36,6 +36,12 @@ class TestAttributeController: public QObject * the sketches play nicely with renaming expressions and metadata gets copied too. */ void testPhotoSketchingSave(); + + /** + * Test that prefillRelationReferenceField sets the rawValue of the FK field in the child controller + * to the parent feature's referenced field value (FID). + */ + void testPrefillRelationReferenceField(); }; #endif // TESTATTRIBUTECONTROLLER_H From 555babb7f50d83a4b56e64ebc288eb44bd4f7b4c Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Mon, 30 Mar 2026 13:13:32 +0300 Subject: [PATCH 4/6] Minor comment --- app/attributes/attributecontroller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/attributes/attributecontroller.cpp b/app/attributes/attributecontroller.cpp index 1b857a2c6..df7e7821a 100644 --- a/app/attributes/attributecontroller.cpp +++ b/app/attributes/attributecontroller.cpp @@ -147,7 +147,7 @@ void AttributeController::prefillRelationReferenceField() } else { - // if thefield is not displayed in the form, then set the attribute directly on the feature + // if the field is not displayed in the form, then set the attribute directly on the feature const QgsVectorLayer *childLayer = mLinkedRelation.referencingLayer(); if ( childLayer ) { From 63e5747beb1a2359de5203fbc18ef1f993acf094 Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Mon, 30 Mar 2026 15:02:33 +0300 Subject: [PATCH 5/6] Added log messages --- app/attributes/attributecontroller.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/attributes/attributecontroller.cpp b/app/attributes/attributecontroller.cpp index df7e7821a..e8697ef42 100644 --- a/app/attributes/attributecontroller.cpp +++ b/app/attributes/attributecontroller.cpp @@ -154,6 +154,12 @@ void AttributeController::prefillRelationReferenceField() const int fieldIndex = childLayer->fields().lookupField( referencingField ); if ( fieldIndex != -1 ) mFeatureLayerPair.featureRef().setAttribute( fieldIndex, foreignKey ); + else + CoreUtils::log( "Attribute Controller - Relations", QStringLiteral( "Could not find field index for field %1" ).arg( referencingField ) ); + } + else + { + CoreUtils::log( "Attribute Controller - Relations", QStringLiteral( "Invalid child layer for relation %1" ).arg( mLinkedRelation.name() ) ); } } } From 6085bc3853852ead71255b82d2b93f2bf0408e33 Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Mon, 30 Mar 2026 15:32:41 +0300 Subject: [PATCH 6/6] Implemented code findings --- app/test/testattributecontroller.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/test/testattributecontroller.cpp b/app/test/testattributecontroller.cpp index e1041a588..2145bb058 100644 --- a/app/test/testattributecontroller.cpp +++ b/app/test/testattributecontroller.cpp @@ -1225,11 +1225,11 @@ void TestAttributeController::testPrefillRelationReferenceField() QString projectDir = TestUtils::testDataDir() + "/planes"; QVERIFY( QgsProject::instance()->read( projectDir + "/quickapp_project.qgs" ) ); - QgsVectorLayer *airportsLayer = static_cast( + QgsVectorLayer *airportsLayer = dynamic_cast( QgsProject::instance()->mapLayersByName( QStringLiteral( "airports" ) ).at( 0 ) ); QVERIFY( airportsLayer && airportsLayer->isValid() ); - QgsVectorLayer *towersLayer = static_cast( + QgsVectorLayer *towersLayer = dynamic_cast( QgsProject::instance()->mapLayersByName( QStringLiteral( "airport-towers" ) ).at( 0 ) ); QVERIFY( towersLayer && towersLayer->isValid() );