Skip to content

Commit

Permalink
Implement representValue() for Relation Reference widget
Browse files Browse the repository at this point in the history
  • Loading branch information
rouault committed May 12, 2016
1 parent 583eaef commit b38a16f
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 1 deletion.
62 changes: 62 additions & 0 deletions src/gui/editorwidgets/qgsrelationreferencefactory.cpp
Expand Up @@ -13,6 +13,7 @@
* *
***************************************************************************/

#include "qgsproject.h"
#include "qgsrelationreferencefactory.h"

#include "qgsrelationreferencewidgetwrapper.h"
Expand Down Expand Up @@ -103,3 +104,64 @@ QMap<const char*, int> QgsRelationReferenceFactory::supportedWidgetTypes()
map.insert( QgsRelationReferenceWidget::staticMetaObject.className(), 10 );
return map;
}

QString QgsRelationReferenceFactory::representValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const
{
Q_UNUSED( cache );

// Some sanity checks
if ( !config.contains( "Relation" ) )
{
QgsDebugMsg( "Missing Relation in configuration" );
return value.toString();
}
QgsRelation relation = QgsProject::instance()->relationManager()->relation( config["Relation"].toString() );
if ( !relation.isValid() )
{
QgsDebugMsg( "Invalid relation" );
return value.toString();
}
QgsVectorLayer* referencingLayer = relation.referencingLayer();
if ( vl != referencingLayer )
{
QgsDebugMsg( "representValue() with inconsistant vl parameter w.r.t relation referencingLayer" );
return value.toString();
}
int referencingFieldIdx = referencingLayer->fieldNameIndex( relation.fieldPairs().at( 0 ).first );
if ( referencingFieldIdx != fieldIdx )
{
QgsDebugMsg( "representValue() with inconsistant fieldIdx parameter w.r.t relation referencingFieldIdx" );
return value.toString();
}
QgsVectorLayer* referencedLayer = relation.referencedLayer();
if ( !referencedLayer )
{
QgsDebugMsg( "Cannot find referenced layer" );
return value.toString();
}

// Attributes from the referencing layer
QgsAttributes attrs = QgsAttributes( vl->fields().count() );
// Set the value on the foreign key field of the referencing record
attrs[ referencingFieldIdx ] = value;

QgsFeatureRequest request = relation.getReferencedFeatureRequest( attrs );
QgsFeature feature;
referencedLayer->getFeatures( request ).nextFeature( feature );
if ( !feature.isValid() )
return value.toString();

QgsExpression expr( referencedLayer->displayExpression() );
QgsExpressionContext context;
context << QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope()
<< QgsExpressionContextUtils::layerScope( referencedLayer );
context.setFeature( feature );
QString title = expr.evaluate( &context ).toString();
if ( expr.hasEvalError() )
{
int referencedFieldIdx = referencedLayer->fieldNameIndex( relation.fieldPairs().at( 0 ).second );
title = feature.attribute( referencedFieldIdx ).toString();
}
return title;
}
2 changes: 2 additions & 0 deletions src/gui/editorwidgets/qgsrelationreferencefactory.h
Expand Up @@ -79,6 +79,8 @@ class GUI_EXPORT QgsRelationReferenceFactory : public QgsEditorWidgetFactory
*/
virtual void writeConfig( const QgsEditorWidgetConfig& config, QDomElement& configElement, QDomDocument& doc, const QgsVectorLayer* layer, int fieldIdx ) override;

QString representValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const override;

virtual QMap<const char*, int> supportedWidgetTypes() override;

private:
Expand Down
135 changes: 134 additions & 1 deletion tests/src/python/test_qgseditwidgets.py
Expand Up @@ -14,7 +14,7 @@

import qgis # NOQA

from qgis.core import QgsFeature, QgsGeometry, QgsPoint, QgsVectorLayer, NULL
from qgis.core import QgsMapLayerRegistry, QgsFeature, QgsGeometry, QgsPoint, QgsProject, QgsRelation, QgsVectorLayer, NULL
from qgis.gui import QgsEditorWidgetRegistry

from qgis.testing import start_app, unittest
Expand Down Expand Up @@ -63,6 +63,139 @@ def test_SetValue(self):
self.doAttributeTest(0, ['value', '123', NULL, NULL])
self.doAttributeTest(1, [NULL, 123, NULL, NULL])

def test_ValueRelation_representValue(self):

first_layer = QgsVectorLayer("none?field=foreign_key:integer",
"first_layer", "memory")
assert first_layer.isValid()
second_layer = QgsVectorLayer("none?field=pkid:integer&field=decoded:string",
"second_layer", "memory")
assert second_layer.isValid()
QgsMapLayerRegistry.instance().addMapLayer(second_layer)
f = QgsFeature()
f.setAttributes([123])
assert first_layer.dataProvider().addFeatures([f])
f = QgsFeature()
f.setAttributes([123, 'decoded_val'])
assert second_layer.dataProvider().addFeatures([f])

reg = QgsEditorWidgetRegistry.instance()
factory = reg.factory("ValueRelation")
self.assertIsNotNone(factory)

# Everything valid
config = {'Layer': second_layer.id(), 'Key': 'pkid', 'Value': 'decoded'}
self.assertEqual(factory.representValue(first_layer, 0, config, None, '123'), 'decoded_val')

# Code not find match in foreign layer
config = {'Layer': second_layer.id(), 'Key': 'pkid', 'Value': 'decoded'}
self.assertEqual(factory.representValue(first_layer, 0, config, None, '456'), '(456)')

# Missing Layer
config = {'Key': 'pkid', 'Value': 'decoded'}
self.assertEqual(factory.representValue(first_layer, 0, config, None, '456'), '(456)')

# Invalid Layer
config = {'Layer': 'invalid', 'Key': 'pkid', 'Value': 'decoded'}
self.assertEqual(factory.representValue(first_layer, 0, config, None, '456'), '(456)')

# Invalid Key
config = {'Layer': second_layer.id(), 'Key': 'invalid', 'Value': 'decoded'}
self.assertEqual(factory.representValue(first_layer, 0, config, None, '456'), '(456)')

# Invalid Value
config = {'Layer': second_layer.id(), 'Key': 'pkid', 'Value': 'invalid'}
self.assertEqual(factory.representValue(first_layer, 0, config, None, '456'), '(456)')

QgsMapLayerRegistry.instance().removeMapLayer(second_layer.id())

def test_RelationReference_representValue(self):

first_layer = QgsVectorLayer("none?field=foreign_key:integer",
"first_layer", "memory")
assert first_layer.isValid()
second_layer = QgsVectorLayer("none?field=pkid:integer&field=decoded:string",
"second_layer", "memory")
assert second_layer.isValid()
QgsMapLayerRegistry.instance().addMapLayers([first_layer, second_layer])
f = QgsFeature()
f.setAttributes([123])
assert first_layer.dataProvider().addFeatures([f])
f = QgsFeature()
f.setAttributes([123, 'decoded_val'])
assert second_layer.dataProvider().addFeatures([f])

relMgr = QgsProject.instance().relationManager()

reg = QgsEditorWidgetRegistry.instance()
factory = reg.factory("RelationReference")
self.assertIsNotNone(factory)

rel = QgsRelation()
rel.setRelationId('rel1')
rel.setRelationName('Relation Number One')
rel.setReferencingLayer(first_layer.id())
rel.setReferencedLayer(second_layer.id())
rel.addFieldPair('foreign_key', 'pkid')
assert(rel.isValid())

relMgr.addRelation(rel)

# Everything valid
config = {'Relation': rel.id()}
second_layer.setDisplayExpression('decoded')
self.assertEqual(factory.representValue(first_layer, 0, config, None, '123'), 'decoded_val')

# Code not find match in foreign layer
config = {'Relation': rel.id()}
second_layer.setDisplayExpression('decoded')
self.assertEqual(factory.representValue(first_layer, 0, config, None, '456'), '456')

# Invalid relation id
config = {'Relation': 'invalid'}
second_layer.setDisplayExpression('decoded')
self.assertEqual(factory.representValue(first_layer, 0, config, None, '123'), '123')

# No display expression
config = {'Relation': rel.id()}
second_layer.setDisplayExpression(None)
self.assertEqual(factory.representValue(first_layer, 0, config, None, '123'), '123')

# Invalid display expression
config = {'Relation': rel.id()}
second_layer.setDisplayExpression('invalid +')
self.assertEqual(factory.representValue(first_layer, 0, config, None, '123'), '123')

# Missing relation
config = {}
second_layer.setDisplayExpression('decoded')
self.assertEqual(factory.representValue(first_layer, 0, config, None, '123'), '123')

# Inconsistent layer provided to representValue()
config = {'Relation': rel.id()}
second_layer.setDisplayExpression('decoded')
self.assertEqual(factory.representValue(second_layer, 0, config, None, '123'), '123')

# Inconsistent idx provided to representValue()
config = {'Relation': rel.id()}
second_layer.setDisplayExpression('decoded')
self.assertEqual(factory.representValue(first_layer, 1, config, None, '123'), '123')

# Invalid relation
rel = QgsRelation()
rel.setRelationId('rel2')
rel.setRelationName('Relation Number Two')
rel.setReferencingLayer(first_layer.id())
rel.addFieldPair('foreign_key', 'pkid')
self.assertFalse(rel.isValid())

relMgr.addRelation(rel)

config = {'Relation': rel.id()}
second_layer.setDisplayExpression('decoded')
self.assertEqual(factory.representValue(first_layer, 0, config, None, '123'), '123')

QgsMapLayerRegistry.instance().removeAllMapLayers()

if __name__ == '__main__':
unittest.main()

0 comments on commit b38a16f

Please sign in to comment.