Skip to content

Commit c1fac42

Browse files
committed
Update all attributes in a single transaction
Fixes #17869
1 parent 77163ba commit c1fac42

16 files changed

+350
-1
lines changed

python/core/qgsvectorlayer.sip.in

+44
Original file line numberDiff line numberDiff line change
@@ -1502,6 +1502,50 @@ Returns true if the feature's attribute was successfully changed.
15021502
.. seealso:: :py:func:`changeGeometry`
15031503

15041504
.. seealso:: :py:func:`updateFeature`
1505+
%End
1506+
1507+
bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skeipDefaultValues = false );
1508+
%Docstring
1509+
Changes attributes' values for a feature (but does not immediately
1510+
commit the changes).
1511+
The ``fid`` argument specifies the ID of the feature to be changed.
1512+
1513+
The new values to be assigned to the fields are given by ``newValues``.
1514+
1515+
If a valid QVariant is specified for a field in ``oldValues``, it will be
1516+
used as the field value in the case of an undo operation corresponding
1517+
to this attribute value change. If an invalid QVariant is used (the
1518+
default behavior), then the feature's current value will be
1519+
automatically retrieved and used. Note that this involves a feature
1520+
request to the underlying data provider, so it is more efficient to
1521+
explicitly pass an oldValue if it is already available.
1522+
1523+
If ``skipDefaultValue`` is set to true, default field values will not
1524+
be updated. This can be used to override default field value
1525+
expressions.
1526+
1527+
Returns true if feature's attributes was successfully changed.
1528+
1529+
.. note::
1530+
1531+
Calls to changeAttributeValues() are only valid for layers in
1532+
which edits have been enabled by a call to startEditing(). Changes made
1533+
to features using this method are not committed to the underlying data
1534+
provider until a commitChanges() call is made. Any uncommitted changes
1535+
can be discarded by calling rollBack().
1536+
1537+
.. seealso:: :py:func:`startEditing`
1538+
1539+
.. seealso:: :py:func:`commitChanges`
1540+
1541+
.. seealso:: :py:func:`changeGeometry`
1542+
1543+
.. seealso:: :py:func:`updateFeature`
1544+
1545+
.. seealso:: :py:func:`changeAttributeValue`
1546+
1547+
1548+
.. versionadded:: 3.0
15051549
%End
15061550

15071551
bool addAttribute( const QgsField &field );

python/core/qgsvectorlayereditbuffer.sip.in

+9
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ Change feature's geometry
5858
virtual bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() );
5959
%Docstring
6060
Changed an attribute value (but does not commit it)
61+
%End
62+
63+
virtual bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues );
64+
%Docstring
65+
Changes values of attributes (but does not commit it).
66+
67+
:return: true if attributes are well updated, false otherwise
68+
69+
.. versionadded:: 3.0
6170
%End
6271

6372
virtual bool addAttribute( const QgsField &field );

python/core/qgsvectorlayereditpassthrough.sip.in

+11
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ class QgsVectorLayerEditPassthrough : QgsVectorLayerEditBuffer
3030

3131
virtual bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() );
3232

33+
34+
virtual bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues );
35+
36+
%Docstring
37+
Changes values of attributes (but does not commit it).
38+
39+
:return: true if attributes are well updated, false otherwise
40+
41+
.. versionadded:: 3.0
42+
%End
43+
3344
virtual bool addAttribute( const QgsField &field );
3445

3546
virtual bool deleteAttribute( int attr );

python/core/qgsvectorlayerjoinbuffer.sip.in

+17
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,23 @@ created if its fields are not empty.
185185
:return: false if an error happened, true otherwise
186186

187187

188+
.. versionadded:: 3.0
189+
%End
190+
191+
bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap() );
192+
%Docstring
193+
Changes attributes' values in joined layers. The feature id given in
194+
parameter is the one added in target layer. If the corresponding joined
195+
feature does not exist in a joined layer, then it's automatically
196+
created if its fields are not empty.
197+
198+
:param fid: The feature id
199+
:param newValues: The new values for attributes
200+
:param oldValues: The old values for attributes
201+
202+
:return: false if an error happened, true otherwise
203+
204+
188205
.. versionadded:: 3.0
189206
%End
190207

python/core/qgsvectorlayerundopassthroughcommand.sip.in

+31
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,37 @@ Constructor for QgsVectorLayerUndoPassthroughCommandChangeAttribute
202202
};
203203

204204

205+
class QgsVectorLayerUndoPassthroughCommandChangeAttributes: QgsVectorLayerUndoPassthroughCommand
206+
{
207+
%Docstring
208+
Undo command for changing attributes' values from a vector layer in transaction group.
209+
210+
.. versionadded:: 3.0
211+
%End
212+
213+
%TypeHeaderCode
214+
#include "qgsvectorlayerundopassthroughcommand.h"
215+
%End
216+
public:
217+
218+
QgsVectorLayerUndoPassthroughCommandChangeAttributes( QgsVectorLayerEditBuffer *buffer /Transfer/, QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap() );
219+
%Docstring
220+
Constructor for QgsVectorLayerUndoPassthroughCommandChangeAttributes
221+
222+
:param buffer: associated edit buffer
223+
:param fid: feature ID of feature
224+
:param newValues: New values for attributes
225+
:param oldValues: Old values for attributes
226+
%End
227+
228+
virtual void undo();
229+
230+
virtual void redo();
231+
232+
233+
};
234+
235+
205236
class QgsVectorLayerUndoPassthroughCommandAddAttribute : QgsVectorLayerUndoPassthroughCommand
206237
{
207238
%Docstring

src/core/qgsvectorlayer.cpp

+58
Original file line numberDiff line numberDiff line change
@@ -2387,6 +2387,64 @@ bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QV
23872387
return result;
23882388
}
23892389

2390+
bool QgsVectorLayer::changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues, bool skipDefaultValues )
2391+
{
2392+
bool result = true;
2393+
2394+
QgsAttributeMap newValuesJoin;
2395+
QgsAttributeMap oldValuesJoin;
2396+
2397+
QgsAttributeMap newValuesNotJoin;
2398+
QgsAttributeMap oldValuesNotJoin;
2399+
2400+
for ( auto it = newValues.constBegin(); it != newValues.constEnd(); ++it )
2401+
{
2402+
const int field = it.key();
2403+
const QVariant newValue = it.value();
2404+
QVariant oldValue;
2405+
2406+
if ( oldValues.contains( field ) )
2407+
oldValue = oldValues[field];
2408+
2409+
switch ( fields().fieldOrigin( field ) )
2410+
{
2411+
case QgsFields::OriginJoin:
2412+
newValuesJoin[field] = newValue;
2413+
oldValuesJoin[field] = oldValue;
2414+
break;
2415+
2416+
case QgsFields::OriginProvider:
2417+
case QgsFields::OriginEdit:
2418+
case QgsFields::OriginExpression:
2419+
{
2420+
newValuesNotJoin[field] = newValue;
2421+
oldValuesNotJoin[field] = oldValue;
2422+
break;
2423+
}
2424+
2425+
case QgsFields::OriginUnknown:
2426+
break;
2427+
}
2428+
}
2429+
2430+
if ( ! newValuesJoin.isEmpty() && mJoinBuffer )
2431+
{
2432+
result = mJoinBuffer->changeAttributeValues( fid, newValuesJoin, oldValuesJoin );
2433+
}
2434+
2435+
if ( ! newValuesNotJoin.isEmpty() && mEditBuffer && mDataProvider )
2436+
{
2437+
result &= mEditBuffer->changeAttributeValues( fid, newValues, oldValues );
2438+
}
2439+
2440+
if ( result && !skipDefaultValues && !mDefaultValueOnUpdateFields.isEmpty() )
2441+
{
2442+
updateDefaultValues( fid );
2443+
}
2444+
2445+
return result;
2446+
}
2447+
23902448
bool QgsVectorLayer::addAttribute( const QgsField &field )
23912449
{
23922450
if ( !mEditBuffer || !mDataProvider )

src/core/qgsvectorlayer.h

+38
Original file line numberDiff line numberDiff line change
@@ -1395,6 +1395,43 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
13951395
*/
13961396
bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false );
13971397

1398+
/**
1399+
* Changes attributes' values for a feature (but does not immediately
1400+
* commit the changes).
1401+
* The \a fid argument specifies the ID of the feature to be changed.
1402+
*
1403+
* The new values to be assigned to the fields are given by \a newValues.
1404+
*
1405+
* If a valid QVariant is specified for a field in \a oldValues, it will be
1406+
* used as the field value in the case of an undo operation corresponding
1407+
* to this attribute value change. If an invalid QVariant is used (the
1408+
* default behavior), then the feature's current value will be
1409+
* automatically retrieved and used. Note that this involves a feature
1410+
* request to the underlying data provider, so it is more efficient to
1411+
* explicitly pass an oldValue if it is already available.
1412+
*
1413+
* If \a skipDefaultValue is set to true, default field values will not
1414+
* be updated. This can be used to override default field value
1415+
* expressions.
1416+
*
1417+
* Returns true if feature's attributes was successfully changed.
1418+
*
1419+
* \note Calls to changeAttributeValues() are only valid for layers in
1420+
* which edits have been enabled by a call to startEditing(). Changes made
1421+
* to features using this method are not committed to the underlying data
1422+
* provider until a commitChanges() call is made. Any uncommitted changes
1423+
* can be discarded by calling rollBack().
1424+
*
1425+
* \see startEditing()
1426+
* \see commitChanges()
1427+
* \see changeGeometry()
1428+
* \see updateFeature()
1429+
* \see changeAttributeValue()
1430+
*
1431+
* \since QGIS 3.0
1432+
*/
1433+
bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skeipDefaultValues = false );
1434+
13981435
/**
13991436
* Add an attribute field (but does not commit it)
14001437
* returns true if the field was added
@@ -2376,6 +2413,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
23762413
//! stores information about uncommitted changes to layer
23772414
QgsVectorLayerEditBuffer *mEditBuffer = nullptr;
23782415
friend class QgsVectorLayerEditBuffer;
2416+
friend class QgsVectorLayerEditPassthrough;
23792417

23802418
//stores information about joined layers
23812419
QgsVectorLayerJoinBuffer *mJoinBuffer = nullptr;

src/core/qgsvectorlayereditbuffer.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,23 @@ bool QgsVectorLayerEditBuffer::changeGeometry( QgsFeatureId fid, const QgsGeomet
208208
return true;
209209
}
210210

211+
bool QgsVectorLayerEditBuffer::changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues )
212+
{
213+
bool success = true;
214+
for ( auto it = newValues.constBegin() ; it != newValues.constEnd(); ++it )
215+
{
216+
const int field = it.key();
217+
const QVariant newValue = it.value();
218+
QVariant oldValue;
219+
220+
if ( oldValues.contains( field ) )
221+
oldValue = oldValues[field];
222+
223+
success &= changeAttributeValue( fid, field, newValue, oldValue );
224+
}
225+
226+
return success;
227+
}
211228

212229
bool QgsVectorLayerEditBuffer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue )
213230
{

src/core/qgsvectorlayereditbuffer.h

+8
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject
6565
//! Changed an attribute value (but does not commit it)
6666
virtual bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() );
6767

68+
/**
69+
* Changes values of attributes (but does not commit it).
70+
* \returns true if attributes are well updated, false otherwise
71+
* \since QGIS 3.0
72+
*/
73+
virtual bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues );
74+
6875
/**
6976
* Add an attribute field (but does not commit it)
7077
returns true if the field was added */
@@ -265,6 +272,7 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject
265272
friend class QgsVectorLayerUndoPassthroughCommandDeleteFeatures;
266273
friend class QgsVectorLayerUndoPassthroughCommandChangeGeometry;
267274
friend class QgsVectorLayerUndoPassthroughCommandChangeAttribute;
275+
friend class QgsVectorLayerUndoPassthroughCommandChangeAttributes;
268276
friend class QgsVectorLayerUndoPassthroughCommandAddAttribute;
269277
friend class QgsVectorLayerUndoPassthroughCommandDeleteAttribute;
270278
friend class QgsVectorLayerUndoPassthroughCommandRenameAttribute;

src/core/qgsvectorlayereditpassthrough.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ bool QgsVectorLayerEditPassthrough::changeAttributeValue( QgsFeatureId fid, int
8686
return modify( new QgsVectorLayerUndoPassthroughCommandChangeAttribute( this, fid, field, newValue ) );
8787
}
8888

89+
bool QgsVectorLayerEditPassthrough::changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues )
90+
{
91+
return modify( new QgsVectorLayerUndoPassthroughCommandChangeAttributes( this, fid, newValues, oldValues ) );
92+
}
93+
8994
bool QgsVectorLayerEditPassthrough::addAttribute( const QgsField &field )
9095
{
9196
return modify( new QgsVectorLayerUndoPassthroughCommandAddAttribute( this, field ) );

src/core/qgsvectorlayereditpassthrough.h

+8
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ class CORE_EXPORT QgsVectorLayerEditPassthrough : public QgsVectorLayerEditBuffe
3838
bool deleteFeatures( const QgsFeatureIds &fids ) override;
3939
bool changeGeometry( QgsFeatureId fid, const QgsGeometry &geom ) override;
4040
bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() ) override;
41+
42+
/**
43+
* Changes values of attributes (but does not commit it).
44+
* \returns true if attributes are well updated, false otherwise
45+
* \since QGIS 3.0
46+
*/
47+
bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues ) override;
48+
4149
bool addAttribute( const QgsField &field ) override;
4250
bool deleteAttribute( int attr ) override;
4351
bool renameAttribute( int attr, const QString &newName ) override;

src/core/qgsvectorlayerjoinbuffer.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,25 @@ bool QgsVectorLayerJoinBuffer::changeAttributeValue( QgsFeatureId fid, int field
637637
return false;
638638
}
639639

640+
bool QgsVectorLayerJoinBuffer::changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues )
641+
{
642+
bool success = true;
643+
644+
for ( auto it = newValues.constBegin(); it != newValues.constEnd(); ++it )
645+
{
646+
const int field = it.key();
647+
const QVariant newValue = it.value();
648+
QVariant oldValue;
649+
650+
if ( oldValues.contains( field ) )
651+
oldValue = oldValues[field];
652+
653+
success &= changeAttributeValue( fid, field, newValue, oldValue );
654+
}
655+
656+
return success;
657+
}
658+
640659
bool QgsVectorLayerJoinBuffer::deleteFeature( QgsFeatureId fid ) const
641660
{
642661
return deleteFeatures( QgsFeatureIds() << fid );

src/core/qgsvectorlayerjoinbuffer.h

+16
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,22 @@ class CORE_EXPORT QgsVectorLayerJoinBuffer : public QObject, public QgsFeatureSi
171171
*/
172172
bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() );
173173

174+
/**
175+
* Changes attributes' values in joined layers. The feature id given in
176+
* parameter is the one added in target layer. If the corresponding joined
177+
* feature does not exist in a joined layer, then it's automatically
178+
* created if its fields are not empty.
179+
*
180+
* \param fid The feature id
181+
* \param newValues The new values for attributes
182+
* \param oldValues The old values for attributes
183+
*
184+
* \returns false if an error happened, true otherwise
185+
*
186+
* \since QGIS 3.0
187+
*/
188+
bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap() );
189+
174190
/**
175191
* Deletes a feature from joined layers. The feature id given in
176192
* parameter is the one coming from the target layer.

0 commit comments

Comments
 (0)