Skip to content

Commit d01424a

Browse files
committed
Add QgsRelation::getReferencedFeature to access parent feature
1 parent 740e3ab commit d01424a

File tree

4 files changed

+171
-75
lines changed

4 files changed

+171
-75
lines changed

python/core/qgsrelation.sip

+11-3
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,22 @@ class QgsRelation
118118
*/
119119
QgsFeatureRequest getRelatedFeaturesRequest( const QgsFeature& feature ) const;
120120

121-
const QString name() const;
121+
QgsFeatureRequest getReferencedFeatureRequest( const QgsFeature& feature ) const;
122+
123+
QgsFeature getReferencedFeature( const QgsFeature& child ) const;
122124

123125
/**
124126
* The id
125127
* @return
126128
*/
127-
const QString& id() const;
129+
QString name() const;
130+
131+
/**
132+
* A (project-wide) unique id for this relation
133+
*
134+
* @return The id
135+
*/
136+
QString id() const;
128137

129138
/**
130139
* Access the referencing (child) layer's id
@@ -182,5 +191,4 @@ class QgsRelation
182191

183192
protected:
184193
void updateRelationStatus();
185-
void runChecks();
186194
};

src/core/qgsrelation.cpp

+58-19
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ void QgsRelation::writeXML( QDomNode &node, QDomDocument &doc ) const
9797
elem.setAttribute( "referencingLayer", mReferencingLayerId );
9898
elem.setAttribute( "referencedLayer", mReferencedLayerId );
9999

100-
foreach ( FieldPair fields, mFieldPairs )
100+
Q_FOREACH ( FieldPair fields, mFieldPairs )
101101
{
102102
QDomElement referenceElem = doc.createElement( "fieldRef" );
103103
referenceElem.setAttribute( "referencingField", fields.first );
@@ -153,22 +153,20 @@ QgsFeatureRequest QgsRelation::getRelatedFeaturesRequest( const QgsFeature& feat
153153
{
154154
QStringList conditions;
155155

156-
foreach ( const QgsRelation::FieldPair& fieldPair, mFieldPairs )
156+
Q_FOREACH ( const QgsRelation::FieldPair& fieldPair, mFieldPairs )
157157
{
158158
int referencingIdx = referencingLayer()->pendingFields().indexFromName( fieldPair.referencingField() );
159159
QgsField referencingField = referencingLayer()->pendingFields().at( referencingIdx );
160160

161-
switch ( referencingField.type() )
161+
if ( referencingField.type() == QVariant::String )
162162
{
163-
case QVariant::String:
164-
// Use quotes
165-
conditions << QString( "\"%1\" = '%2'" ).arg( fieldPair.referencingField(), feature.attribute( fieldPair.referencedField() ).toString() );
166-
break;
167-
168-
default:
169-
// No quotes
170-
conditions << QString( "\"%1\" = %2" ).arg( fieldPair.referencingField(), feature.attribute( fieldPair.referencedField() ).toString() );
171-
break;
163+
// Use quotes
164+
conditions << QString( "\"%1\" = '%2'" ).arg( fieldPair.referencingField(), feature.attribute( fieldPair.referencedField() ).toString() );
165+
}
166+
else
167+
{
168+
// No quotes
169+
conditions << QString( "\"%1\" = %2" ).arg( fieldPair.referencingField(), feature.attribute( fieldPair.referencedField() ).toString() );
172170
}
173171
}
174172

@@ -181,12 +179,58 @@ QgsFeatureRequest QgsRelation::getRelatedFeaturesRequest( const QgsFeature& feat
181179
return myRequest;
182180
}
183181

184-
const QString QgsRelation::name() const
182+
QgsFeatureRequest QgsRelation::getReferencedFeatureRequest( const QgsAttributes& attributes ) const
183+
{
184+
QStringList conditions;
185+
186+
Q_FOREACH ( const QgsRelation::FieldPair& fieldPair, mFieldPairs )
187+
{
188+
int referencedIdx = referencedLayer()->pendingFields().indexFromName( fieldPair.referencedField() );
189+
int referencingIdx = referencingLayer()->pendingFields().indexFromName( fieldPair.referencingField() );
190+
191+
QgsField referencedField = referencedLayer()->pendingFields().at( referencedIdx );
192+
193+
if ( referencedField.type() == QVariant::String )
194+
{
195+
// Use quotes
196+
conditions << QString( "\"%1\" = '%2'" ).arg( fieldPair.referencedField(), attributes[referencingIdx].toString() );
197+
}
198+
else
199+
{
200+
// No quotes
201+
conditions << QString( "\"%1\" = %2" ).arg( fieldPair.referencedField(), attributes[referencingIdx].toString() );
202+
}
203+
}
204+
205+
QgsFeatureRequest myRequest;
206+
207+
QgsDebugMsg( QString( "Filter conditions: '%1'" ).arg( conditions.join( " AND " ) ) );
208+
209+
myRequest.setFilterExpression( conditions.join( " AND " ) );
210+
211+
return myRequest;
212+
}
213+
214+
QgsFeatureRequest QgsRelation::getReferencedFeatureRequest( const QgsFeature& feature ) const
215+
{
216+
return getReferencedFeatureRequest( feature.attributes() );
217+
}
218+
219+
QgsFeature QgsRelation::getReferencedFeature( const QgsFeature& child ) const
220+
{
221+
QgsFeatureRequest request = getReferencedFeatureRequest( child );
222+
223+
QgsFeature f;
224+
mReferencedLayer->getFeatures( request ).nextFeature( f );
225+
return f;
226+
}
227+
228+
QString QgsRelation::name() const
185229
{
186230
return mRelationName;
187231
}
188232

189-
const QString& QgsRelation::id() const
233+
QString QgsRelation::id() const
190234
{
191235
return mRelationId;
192236
}
@@ -251,8 +295,3 @@ void QgsRelation::updateRelationStatus()
251295
}
252296
}
253297
}
254-
255-
void QgsRelation::runChecks()
256-
{
257-
258-
}

src/core/qgsrelation.h

+43-6
Original file line numberDiff line numberDiff line change
@@ -138,17 +138,55 @@ class CORE_EXPORT QgsRelation
138138
*
139139
* @param feature A feature from the referenced (parent) layer
140140
*
141-
* @return An request for all the referenced features
141+
* @return A request for all the referencing features
142142
*/
143143
QgsFeatureRequest getRelatedFeaturesRequest( const QgsFeature& feature ) const;
144144

145-
const QString name() const;
145+
/**
146+
* Creates a request to return the feature on the referenced (parent) layer
147+
* which is referenced by the provided feature.
148+
*
149+
* @param attributes An attribute vector containing the foreign key
150+
*
151+
* @return A request the referenced feature
152+
*/
153+
QgsFeatureRequest getReferencedFeatureRequest( const QgsAttributes& attributes ) const;
154+
155+
/**
156+
* Creates a request to return the feature on the referenced (parent) layer
157+
* which is referenced by the provided feature.
158+
*
159+
* @param feature A feature from the referencing (child) layer
160+
*
161+
* @return A request the referenced feature
162+
*/
163+
QgsFeatureRequest getReferencedFeatureRequest( const QgsFeature& feature ) const;
164+
165+
/**
166+
* Creates a request to return the feature on the referenced (parent) layer
167+
* which is referenced by the provided feature.
168+
*
169+
* @param feature A feature from the referencing (child) layer
170+
*
171+
* @return A request the referenced feature
172+
*/
173+
QgsFeature getReferencedFeature( const QgsFeature& child ) const;
174+
175+
/**
176+
* Returns a human readable name for this relation. Mostly used as title for the children.
177+
*
178+
* @see id()
179+
*
180+
* @return A name
181+
*/
182+
QString name() const;
146183

147184
/**
148-
* The id
149-
* @return
185+
* A (project-wide) unique id for this relation
186+
*
187+
* @return The id
150188
*/
151-
const QString& id() const;
189+
QString id() const;
152190

153191
/**
154192
* Access the referencing (child) layer's id
@@ -198,7 +236,6 @@ class CORE_EXPORT QgsRelation
198236

199237
protected:
200238
void updateRelationStatus();
201-
void runChecks();
202239

203240
private:
204241
/** Unique Id */

tests/src/python/test_qgsrelation.py

+59-47
Original file line numberDiff line numberDiff line change
@@ -25,110 +25,122 @@
2525
TestCase,
2626
unittest
2727
)
28+
2829
QGISAPP, CANVAS, IFACE, PARENT = getQgisTestApp()
2930

31+
3032
def createReferencingLayer():
3133
layer = QgsVectorLayer("Point?field=fldtxt:string&field=foreignkey:integer",
3234
"referencinglayer", "memory")
3335
pr = layer.dataProvider()
3436
f1 = QgsFeature()
35-
f1.setFields( layer.pendingFields() )
37+
f1.setFields(layer.pendingFields())
3638
f1.setAttributes(["test1", 123])
37-
f1.setGeometry(QgsGeometry.fromPoint(QgsPoint(100,200)))
39+
f1.setGeometry(QgsGeometry.fromPoint(QgsPoint(100, 200)))
3840
f2 = QgsFeature()
39-
f2.setFields( layer.pendingFields() )
41+
f2.setFields(layer.pendingFields())
4042
f2.setAttributes(["test2", 123])
41-
f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(101,201)))
42-
assert pr.addFeatures([f1,f2])
43+
f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(101, 201)))
44+
assert pr.addFeatures([f1, f2])
4345
return layer
4446

47+
4548
def createReferencedLayer():
4649
layer = QgsVectorLayer(
4750
"Point?field=x:string&field=y:integer&field=z:integer",
4851
"referencedlayer", "memory")
4952
pr = layer.dataProvider()
5053
f1 = QgsFeature()
51-
f1.setFields( layer.pendingFields() )
54+
f1.setFields(layer.pendingFields())
5255
f1.setAttributes(["foo", 123, 321])
53-
f1.setGeometry(QgsGeometry.fromPoint(QgsPoint(1,1)))
56+
f1.setGeometry(QgsGeometry.fromPoint(QgsPoint(1, 1)))
5457
f2 = QgsFeature()
55-
f2.setFields( layer.pendingFields() )
58+
f2.setFields(layer.pendingFields())
5659
f2.setAttributes(["bar", 456, 654])
57-
f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(2,2)))
60+
f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 2)))
5861
f3 = QgsFeature()
59-
f3.setFields( layer.pendingFields() )
62+
f3.setFields(layer.pendingFields())
6063
f3.setAttributes(["foobar", 789, 554])
61-
f3.setGeometry(QgsGeometry.fromPoint(QgsPoint(2,3)))
64+
f3.setGeometry(QgsGeometry.fromPoint(QgsPoint(2, 3)))
6265
assert pr.addFeatures([f1, f2, f3])
6366
return layer
6467

68+
6569
def formatAttributes(attrs):
66-
return repr([ unicode(a) for a in attrs ])
70+
return repr([unicode(a) for a in attrs])
71+
6772

68-
class TestQgsRelation( TestCase ):
73+
class TestQgsRelation(TestCase):
74+
def setUp(self):
75+
self.referencedLayer = createReferencedLayer()
76+
self.referencingLayer = createReferencingLayer()
77+
QgsMapLayerRegistry.instance().addMapLayers([self.referencedLayer, self.referencingLayer])
78+
79+
def tearDown(self):
80+
QgsMapLayerRegistry.instance().removeAllMapLayers()
6981

7082
def test_isValid(self):
71-
referencedLayer = createReferencedLayer()
72-
referencingLayer = createReferencingLayer()
73-
QgsMapLayerRegistry.instance().addMapLayers([referencedLayer,referencingLayer])
7483

7584
rel = QgsRelation()
7685
assert not rel.isValid()
7786

78-
rel.setRelationId( 'rel1' )
87+
rel.setRelationId('rel1')
7988
assert not rel.isValid()
8089

81-
rel.setRelationName( 'Relation Number One' )
90+
rel.setRelationName('Relation Number One')
8291
assert not rel.isValid()
8392

84-
rel.setReferencingLayer( referencingLayer.id() )
93+
rel.setReferencingLayer(self.referencingLayer.id())
8594
assert not rel.isValid()
8695

87-
rel.setReferencedLayer( referencedLayer.id() )
96+
rel.setReferencedLayer(self.referencedLayer.id())
8897
assert not rel.isValid()
8998

90-
rel.addFieldPair( 'foreignkey', 'y' )
99+
rel.addFieldPair('foreignkey', 'y')
91100
assert rel.isValid()
92101

93-
QgsMapLayerRegistry.instance().removeAllMapLayers()
94102

95103
def test_getRelatedFeatures(self):
96-
referencedLayer = createReferencedLayer()
97-
referencingLayer = createReferencingLayer()
98-
QgsMapLayerRegistry.instance().addMapLayers([referencedLayer,referencingLayer])
99-
100104
rel = QgsRelation()
101105

102-
rel.setRelationId( 'rel1' )
103-
rel.setRelationName( 'Relation Number One' )
104-
rel.setReferencingLayer( referencingLayer.id() )
105-
rel.setReferencedLayer( referencedLayer.id() )
106-
rel.addFieldPair( 'foreignkey', 'y' )
106+
rel.setRelationId('rel1')
107+
rel.setRelationName('Relation Number One')
108+
rel.setReferencingLayer(self.referencingLayer.id())
109+
rel.setReferencedLayer(self.referencedLayer.id())
110+
rel.addFieldPair('foreignkey', 'y')
107111

108-
feat = referencedLayer.getFeatures().next()
112+
feat = self.referencedLayer.getFeatures().next()
109113

110-
it = rel.getRelatedFeatures( feat )
114+
it = rel.getRelatedFeatures(feat)
111115

112-
[ a.attributes() for a in it ] == [[u'test1', 123], [u'test2', 123]]
116+
assert [a.attributes() for a in it] == [[u'test1', 123], [u'test2', 123]]
113117

114-
QgsMapLayerRegistry.instance().removeAllMapLayers()
118+
def test_getReferencedFeature(self):
119+
rel = QgsRelation()
120+
rel.setRelationId('rel1')
121+
rel.setRelationName('Relation Number One')
122+
rel.setReferencingLayer(self.referencingLayer.id())
123+
rel.setReferencedLayer(self.referencedLayer.id())
124+
rel.addFieldPair('foreignkey', 'y')
115125

116-
def test_fieldPairs(self):
117-
referencedLayer = createReferencedLayer()
118-
referencingLayer = createReferencingLayer()
119-
QgsMapLayerRegistry.instance().addMapLayers([referencedLayer,referencingLayer])
126+
feat = self.referencingLayer.getFeatures().next()
120127

121-
rel = QgsRelation()
128+
f = rel.getReferencedFeature(feat)
122129

123-
rel.setRelationId( 'rel1' )
124-
rel.setRelationName( 'Relation Number One' )
125-
rel.setReferencingLayer( referencingLayer.id() )
126-
rel.setReferencedLayer( referencedLayer.id() )
127-
rel.addFieldPair( 'foreignkey', 'y' )
130+
assert f.isValid()
131+
assert f[0] == 'foo'
128132

129-
assert( rel.fieldPairs() == { 'foreignkey': 'y'} )
130133

131-
QgsMapLayerRegistry.instance().removeAllMapLayers()
134+
def test_fieldPairs(self):
135+
rel = QgsRelation()
136+
137+
rel.setRelationId('rel1')
138+
rel.setRelationName('Relation Number One')
139+
rel.setReferencingLayer(self.referencingLayer.id())
140+
rel.setReferencedLayer(self.referencedLayer.id())
141+
rel.addFieldPair('foreignkey', 'y')
142+
143+
assert (rel.fieldPairs() == {'foreignkey': 'y'})
132144

133145

134146
if __name__ == '__main__':

0 commit comments

Comments
 (0)