Skip to content

Commit

Permalink
Merge pull request #6142 from pblottiere/bugfix_transaction_constraints
Browse files Browse the repository at this point in the history
Update all attributes in a single transaction
  • Loading branch information
pblottiere committed Jan 29, 2018
2 parents cdf697d + e24b6bb commit aec399e
Show file tree
Hide file tree
Showing 18 changed files with 405 additions and 4 deletions.
46 changes: 45 additions & 1 deletion python/core/qgsvectorlayer.sip.in
Expand Up @@ -1483,7 +1483,7 @@ QVariant is used (the default behavior), then the feature's current value will b
retrieved and used. Note that this involves a feature request to the underlying data provider,
so it is more efficient to explicitly pass an ``oldValue`` if it is already available.

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

Returns true if the feature's attribute was successfully changed.
Expand All @@ -1502,6 +1502,50 @@ Returns true if the feature's attribute was successfully changed.
.. seealso:: :py:func:`changeGeometry`

.. seealso:: :py:func:`updateFeature`
%End

bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skipDefaultValues = false );
%Docstring
Changes attributes' values for a feature (but does not immediately
commit the changes).
The ``fid`` argument specifies the ID of the feature to be changed.

The new values to be assigned to the fields are given by ``newValues``.

If a valid QVariant is specified for a field in ``oldValues``, it will be
used as the field value in the case of an undo operation corresponding
to this attribute value change. If an invalid QVariant is used (the
default behavior), then the feature's current value will be
automatically retrieved and used. Note that this involves a feature
request to the underlying data provider, so it is more efficient to
explicitly pass an oldValue if it is already available.

If ``skipDefaultValues`` is set to true, default field values will not
be updated. This can be used to override default field value
expressions.

Returns true if feature's attributes was successfully changed.

.. note::

Calls to changeAttributeValues() are only valid for layers in
which edits have been enabled by a call to startEditing(). Changes made
to features using this method are not committed to the underlying data
provider until a commitChanges() call is made. Any uncommitted changes
can be discarded by calling rollBack().

.. seealso:: :py:func:`startEditing`

.. seealso:: :py:func:`commitChanges`

.. seealso:: :py:func:`changeGeometry`

.. seealso:: :py:func:`updateFeature`

.. seealso:: :py:func:`changeAttributeValue`


.. versionadded:: 3.0
%End

bool addAttribute( const QgsField &field );
Expand Down
9 changes: 9 additions & 0 deletions python/core/qgsvectorlayereditbuffer.sip.in
Expand Up @@ -58,6 +58,15 @@ Change feature's geometry
virtual bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() );
%Docstring
Changed an attribute value (but does not commit it)
%End

virtual bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues );
%Docstring
Changes values of attributes (but does not commit it).

:return: true if attributes are well updated, false otherwise

.. versionadded:: 3.0
%End

virtual bool addAttribute( const QgsField &field );
Expand Down
11 changes: 11 additions & 0 deletions python/core/qgsvectorlayereditpassthrough.sip.in
Expand Up @@ -30,6 +30,17 @@ class QgsVectorLayerEditPassthrough : QgsVectorLayerEditBuffer

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


virtual bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues );

%Docstring
Changes values of attributes (but does not commit it).

:return: true if attributes are well updated, false otherwise

.. versionadded:: 3.0
%End

virtual bool addAttribute( const QgsField &field );

virtual bool deleteAttribute( int attr );
Expand Down
17 changes: 17 additions & 0 deletions python/core/qgsvectorlayerjoinbuffer.sip.in
Expand Up @@ -185,6 +185,23 @@ created if its fields are not empty.
:return: false if an error happened, true otherwise


.. versionadded:: 3.0
%End

bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap() );
%Docstring
Changes attributes' values in joined layers. The feature id given in
parameter is the one added in target layer. If the corresponding joined
feature does not exist in a joined layer, then it's automatically
created if its fields are not empty.

:param fid: The feature id
:param newValues: The new values for attributes
:param oldValues: The old values for attributes

:return: false if an error happened, true otherwise


.. versionadded:: 3.0
%End

Expand Down
31 changes: 31 additions & 0 deletions python/core/qgsvectorlayerundopassthroughcommand.sip.in
Expand Up @@ -202,6 +202,37 @@ Constructor for QgsVectorLayerUndoPassthroughCommandChangeAttribute
};


class QgsVectorLayerUndoPassthroughCommandChangeAttributes: QgsVectorLayerUndoPassthroughCommand
{
%Docstring
Undo command for changing attributes' values from a vector layer in transaction group.

.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsvectorlayerundopassthroughcommand.h"
%End
public:

QgsVectorLayerUndoPassthroughCommandChangeAttributes( QgsVectorLayerEditBuffer *buffer /Transfer/, QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap() );
%Docstring
Constructor for QgsVectorLayerUndoPassthroughCommandChangeAttributes

:param buffer: associated edit buffer
:param fid: feature ID of feature
:param newValues: New values for attributes
:param oldValues: Old values for attributes
%End

virtual void undo();

virtual void redo();


};


class QgsVectorLayerUndoPassthroughCommandAddAttribute : QgsVectorLayerUndoPassthroughCommand
{
%Docstring
Expand Down
58 changes: 58 additions & 0 deletions src/core/qgsvectorlayer.cpp
Expand Up @@ -2387,6 +2387,64 @@ bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QV
return result;
}

bool QgsVectorLayer::changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues, bool skipDefaultValues )
{
bool result = true;

QgsAttributeMap newValuesJoin;
QgsAttributeMap oldValuesJoin;

QgsAttributeMap newValuesNotJoin;
QgsAttributeMap oldValuesNotJoin;

for ( auto it = newValues.constBegin(); it != newValues.constEnd(); ++it )
{
const int field = it.key();
const QVariant newValue = it.value();
QVariant oldValue;

if ( oldValues.contains( field ) )
oldValue = oldValues[field];

switch ( fields().fieldOrigin( field ) )
{
case QgsFields::OriginJoin:
newValuesJoin[field] = newValue;
oldValuesJoin[field] = oldValue;
break;

case QgsFields::OriginProvider:
case QgsFields::OriginEdit:
case QgsFields::OriginExpression:
{
newValuesNotJoin[field] = newValue;
oldValuesNotJoin[field] = oldValue;
break;
}

case QgsFields::OriginUnknown:
break;
}
}

if ( ! newValuesJoin.isEmpty() && mJoinBuffer )
{
result = mJoinBuffer->changeAttributeValues( fid, newValuesJoin, oldValuesJoin );
}

if ( ! newValuesNotJoin.isEmpty() && mEditBuffer && mDataProvider )
{
result &= mEditBuffer->changeAttributeValues( fid, newValues, oldValues );
}

if ( result && !skipDefaultValues && !mDefaultValueOnUpdateFields.isEmpty() )
{
updateDefaultValues( fid );
}

return result;
}

bool QgsVectorLayer::addAttribute( const QgsField &field )
{
if ( !mEditBuffer || !mDataProvider )
Expand Down
40 changes: 39 additions & 1 deletion src/core/qgsvectorlayer.h
Expand Up @@ -1378,7 +1378,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
* retrieved and used. Note that this involves a feature request to the underlying data provider,
* so it is more efficient to explicitly pass an \a oldValue if it is already available.
*
* If \a skipDefaultValue is set to true, default field values will not
* If \a skipDefaultValues is set to true, default field values will not
* be updated. This can be used to override default field value expressions.
*
* Returns true if the feature's attribute was successfully changed.
Expand All @@ -1395,6 +1395,43 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
*/
bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false );

/**
* Changes attributes' values for a feature (but does not immediately
* commit the changes).
* The \a fid argument specifies the ID of the feature to be changed.
*
* The new values to be assigned to the fields are given by \a newValues.
*
* If a valid QVariant is specified for a field in \a oldValues, it will be
* used as the field value in the case of an undo operation corresponding
* to this attribute value change. If an invalid QVariant is used (the
* default behavior), then the feature's current value will be
* automatically retrieved and used. Note that this involves a feature
* request to the underlying data provider, so it is more efficient to
* explicitly pass an oldValue if it is already available.
*
* If \a skipDefaultValues is set to true, default field values will not
* be updated. This can be used to override default field value
* expressions.
*
* Returns true if feature's attributes was successfully changed.
*
* \note Calls to changeAttributeValues() are only valid for layers in
* which edits have been enabled by a call to startEditing(). Changes made
* to features using this method are not committed to the underlying data
* provider until a commitChanges() call is made. Any uncommitted changes
* can be discarded by calling rollBack().
*
* \see startEditing()
* \see commitChanges()
* \see changeGeometry()
* \see updateFeature()
* \see changeAttributeValue()
*
* \since QGIS 3.0
*/
bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skipDefaultValues = false );

/**
* Add an attribute field (but does not commit it)
* returns true if the field was added
Expand Down Expand Up @@ -2376,6 +2413,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
//! stores information about uncommitted changes to layer
QgsVectorLayerEditBuffer *mEditBuffer = nullptr;
friend class QgsVectorLayerEditBuffer;
friend class QgsVectorLayerEditPassthrough;

//stores information about joined layers
QgsVectorLayerJoinBuffer *mJoinBuffer = nullptr;
Expand Down
17 changes: 17 additions & 0 deletions src/core/qgsvectorlayereditbuffer.cpp
Expand Up @@ -208,6 +208,23 @@ bool QgsVectorLayerEditBuffer::changeGeometry( QgsFeatureId fid, const QgsGeomet
return true;
}

bool QgsVectorLayerEditBuffer::changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues )
{
bool success = true;
for ( auto it = newValues.constBegin() ; it != newValues.constEnd(); ++it )
{
const int field = it.key();
const QVariant newValue = it.value();
QVariant oldValue;

if ( oldValues.contains( field ) )
oldValue = oldValues[field];

success &= changeAttributeValue( fid, field, newValue, oldValue );
}

return success;
}

bool QgsVectorLayerEditBuffer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue )
{
Expand Down
8 changes: 8 additions & 0 deletions src/core/qgsvectorlayereditbuffer.h
Expand Up @@ -65,6 +65,13 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject
//! Changed an attribute value (but does not commit it)
virtual bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() );

/**
* Changes values of attributes (but does not commit it).
* \returns true if attributes are well updated, false otherwise
* \since QGIS 3.0
*/
virtual bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues );

/**
* Add an attribute field (but does not commit it)
returns true if the field was added */
Expand Down Expand Up @@ -265,6 +272,7 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject
friend class QgsVectorLayerUndoPassthroughCommandDeleteFeatures;
friend class QgsVectorLayerUndoPassthroughCommandChangeGeometry;
friend class QgsVectorLayerUndoPassthroughCommandChangeAttribute;
friend class QgsVectorLayerUndoPassthroughCommandChangeAttributes;
friend class QgsVectorLayerUndoPassthroughCommandAddAttribute;
friend class QgsVectorLayerUndoPassthroughCommandDeleteAttribute;
friend class QgsVectorLayerUndoPassthroughCommandRenameAttribute;
Expand Down
5 changes: 5 additions & 0 deletions src/core/qgsvectorlayereditpassthrough.cpp
Expand Up @@ -86,6 +86,11 @@ bool QgsVectorLayerEditPassthrough::changeAttributeValue( QgsFeatureId fid, int
return modify( new QgsVectorLayerUndoPassthroughCommandChangeAttribute( this, fid, field, newValue ) );
}

bool QgsVectorLayerEditPassthrough::changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues )
{
return modify( new QgsVectorLayerUndoPassthroughCommandChangeAttributes( this, fid, newValues, oldValues ) );
}

bool QgsVectorLayerEditPassthrough::addAttribute( const QgsField &field )
{
return modify( new QgsVectorLayerUndoPassthroughCommandAddAttribute( this, field ) );
Expand Down
8 changes: 8 additions & 0 deletions src/core/qgsvectorlayereditpassthrough.h
Expand Up @@ -38,6 +38,14 @@ class CORE_EXPORT QgsVectorLayerEditPassthrough : public QgsVectorLayerEditBuffe
bool deleteFeatures( const QgsFeatureIds &fids ) override;
bool changeGeometry( QgsFeatureId fid, const QgsGeometry &geom ) override;
bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() ) override;

/**
* Changes values of attributes (but does not commit it).
* \returns true if attributes are well updated, false otherwise
* \since QGIS 3.0
*/
bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues ) override;

bool addAttribute( const QgsField &field ) override;
bool deleteAttribute( int attr ) override;
bool renameAttribute( int attr, const QString &newName ) override;
Expand Down
19 changes: 19 additions & 0 deletions src/core/qgsvectorlayerjoinbuffer.cpp
Expand Up @@ -637,6 +637,25 @@ bool QgsVectorLayerJoinBuffer::changeAttributeValue( QgsFeatureId fid, int field
return false;
}

bool QgsVectorLayerJoinBuffer::changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues )
{
bool success = true;

for ( auto it = newValues.constBegin(); it != newValues.constEnd(); ++it )
{
const int field = it.key();
const QVariant newValue = it.value();
QVariant oldValue;

if ( oldValues.contains( field ) )
oldValue = oldValues[field];

success &= changeAttributeValue( fid, field, newValue, oldValue );
}

return success;
}

bool QgsVectorLayerJoinBuffer::deleteFeature( QgsFeatureId fid ) const
{
return deleteFeatures( QgsFeatureIds() << fid );
Expand Down

0 comments on commit aec399e

Please sign in to comment.