Merge pull request #7315 from m-kuhn/confirmDeleteLinkedNm

Confirm delete when feature is still linked
This commit is contained in:
Matthias Kuhn 2018-07-31 17:49:29 +02:00 committed by GitHub
commit 01bf140763
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 97 additions and 4 deletions

View File

@ -33,6 +33,7 @@
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
QgsRelationEditorWidget::QgsRelationEditorWidget( QWidget *parent )
: QgsCollapsibleGroupBox( parent )
@ -492,9 +493,84 @@ void QgsRelationEditorWidget::deleteSelectedFeatures()
void QgsRelationEditorWidget::deleteFeatures( const QgsFeatureIds &featureids )
{
QgsVectorLayer *layer = mNmRelation.isValid() ? mNmRelation.referencedLayer() : mRelation.referencingLayer();
layer->deleteFeatures( featureids );
updateUi();
bool deleteFeatures = true;
QgsVectorLayer *layer;
if ( mNmRelation.isValid() )
{
layer = mNmRelation.referencedLayer();
// When deleting a linked feature within an N:M relation,
// check if the feature is linked to more than just one feature.
// In case it is linked more than just once, ask the user for confirmation
// as it is likely he was not aware of the implications and might either
// leave the dataset in a corrupted state (referential integrity) or if
// the fk constraint is ON CASCADE DELETE, there may be several linking
// entries deleted along.
QgsFeatureRequest deletedFeaturesRequest;
deletedFeaturesRequest.setFilterFids( featureids );
deletedFeaturesRequest.setFlags( QgsFeatureRequest::NoGeometry );
deletedFeaturesRequest.setSubsetOfAttributes( QgsAttributeList() << mNmRelation.referencedFields().first() );
QgsFeatureIterator deletedFeatures = layer->getFeatures( deletedFeaturesRequest );
QStringList deletedFeaturesPks;
QgsFeature feature;
while ( deletedFeatures.nextFeature( feature ) )
{
deletedFeaturesPks.append( QgsExpression::quotedValue( feature.attribute( mNmRelation.referencedFields().first() ) ) );
}
QgsFeatureRequest linkingFeaturesRequest;
linkingFeaturesRequest.setFlags( QgsFeatureRequest::NoGeometry );
linkingFeaturesRequest.setSubsetOfAttributes( QgsAttributeList() );
QString linkingFeaturesRequestExpression;
if ( !deletedFeaturesPks.empty() )
{
linkingFeaturesRequestExpression = QStringLiteral( "%1 IN (%2)" ).arg( QgsExpression::quotedColumnRef( mNmRelation.fieldPairs().first().first ), deletedFeaturesPks.join( ',' ) );
linkingFeaturesRequest.setFilterExpression( linkingFeaturesRequestExpression );
QgsFeatureIterator relatedLinkingFeatures = mNmRelation.referencingLayer()->getFeatures( linkingFeaturesRequest );
int relatedLinkingFeaturesCount = 0;
while ( relatedLinkingFeatures.nextFeature( feature ) )
{
relatedLinkingFeaturesCount++;
}
if ( deletedFeaturesPks.size() == 1 && relatedLinkingFeaturesCount > 1 )
{
QMessageBox messageBox( QMessageBox::Question, tr( "Really delete entry?" ), tr( "The entry on %1 is still linked to %2 features on %3. Do you want to delete it?" ).arg( mNmRelation.referencedLayer()->name(), QString::number( relatedLinkingFeaturesCount ), mRelation.referencedLayer()->name() ), QMessageBox::NoButton, this );
messageBox.addButton( QMessageBox::Cancel );
QAbstractButton *deleteButton = messageBox.addButton( tr( "Delete" ), QMessageBox::AcceptRole );
messageBox.exec();
if ( messageBox.clickedButton() != deleteButton )
deleteFeatures = false;
}
else if ( deletedFeaturesPks.size() > 1 && relatedLinkingFeaturesCount > deletedFeaturesPks.size() )
{
QMessageBox messageBox( QMessageBox::Question, tr( "Really delete entries?" ), tr( "The %1 entries on %2 are still linked to %3 features on %4. Do you want to delete them?" ).arg( QString::number( deletedFeaturesPks.size() ), mNmRelation.referencedLayer()->name(), QString::number( relatedLinkingFeaturesCount ), mRelation.referencedLayer()->name() ), QMessageBox::NoButton, this );
messageBox.addButton( QMessageBox::Cancel );
QAbstractButton *deleteButton = messageBox.addButton( tr( "Delete" ), QMessageBox::AcceptRole );
messageBox.exec();
if ( messageBox.clickedButton() != deleteButton )
deleteFeatures = false;
}
}
}
else
{
layer = mRelation.referencingLayer();
}
if ( deleteFeatures )
{
layer->deleteFeatures( featureids );
updateUi();
}
}
void QgsRelationEditorWidget::unlinkFeature( const QgsFeatureId featureid )

View File

@ -34,7 +34,13 @@ from qgis.gui import (
)
from qgis.PyQt.QtCore import QTimer
from qgis.PyQt.QtWidgets import QToolButton, QTableView, QApplication
from qgis.PyQt.QtWidgets import (
QToolButton,
QMessageBox,
QDialogButtonBox,
QTableView,
QApplication
)
from qgis.testing import start_app, unittest
start_app()
@ -92,6 +98,7 @@ class TestQgsRelationEditWidget(unittest.TestCase):
def tearDown(self):
self.rollbackTransaction()
del self.transaction
def test_delete_feature(self):
"""
@ -108,6 +115,16 @@ class TestQgsRelationEditWidget(unittest.TestCase):
self.widget.featureSelectionManager().select([fid])
btn = self.widget.findChild(QToolButton, 'mDeleteFeatureButton')
def clickOk():
# Click the "Delete features" button on the confirmation message
# box
widget = self.widget.findChild(QMessageBox)
buttonBox = widget.findChild(QDialogButtonBox)
deleteButton = next((b for b in buttonBox.buttons() if buttonBox.buttonRole(b) == QDialogButtonBox.AcceptRole))
deleteButton.click()
QTimer.singleShot(1, clickOk)
btn.click()
# This is the important check that the feature is deleted