Skip to content

Commit aec399e

Browse files
authored
Merge pull request #6142 from pblottiere/bugfix_transaction_constraints
Update all attributes in a single transaction
2 parents cdf697d + e24b6bb commit aec399e

18 files changed

+405
-4
lines changed

python/core/qgsvectorlayer.sip.in

+45-1
Original file line numberDiff line numberDiff line change
@@ -1483,7 +1483,7 @@ QVariant is used (the default behavior), then the feature's current value will b
14831483
retrieved and used. Note that this involves a feature request to the underlying data provider,
14841484
so it is more efficient to explicitly pass an ``oldValue`` if it is already available.
14851485

1486-
If ``skipDefaultValue`` is set to true, default field values will not
1486+
If ``skipDefaultValues`` is set to true, default field values will not
14871487
be updated. This can be used to override default field value expressions.
14881488

14891489
Returns true if the feature's attribute was successfully changed.
@@ -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 skipDefaultValues = 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 ``skipDefaultValues`` 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

+39-1
Original file line numberDiff line numberDiff line change
@@ -1378,7 +1378,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
13781378
* retrieved and used. Note that this involves a feature request to the underlying data provider,
13791379
* so it is more efficient to explicitly pass an \a oldValue if it is already available.
13801380
*
1381-
* If \a skipDefaultValue is set to true, default field values will not
1381+
* If \a skipDefaultValues is set to true, default field values will not
13821382
* be updated. This can be used to override default field value expressions.
13831383
*
13841384
* Returns true if the feature's attribute was successfully changed.
@@ -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 skipDefaultValues 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 skipDefaultValues = 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 );

0 commit comments

Comments
 (0)