Skip to content

Commit 01bf140

Browse files
authoredJul 31, 2018
Merge pull request #7315 from m-kuhn/confirmDeleteLinkedNm
Confirm delete when feature is still linked
2 parents c1eef4e + 4ce0182 commit 01bf140

File tree

2 files changed

+97
-4
lines changed

2 files changed

+97
-4
lines changed
 

‎src/gui/qgsrelationeditorwidget.cpp

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
#include <QHBoxLayout>
3535
#include <QLabel>
36+
#include <QMessageBox>
3637

3738
QgsRelationEditorWidget::QgsRelationEditorWidget( QWidget *parent )
3839
: QgsCollapsibleGroupBox( parent )
@@ -492,9 +493,84 @@ void QgsRelationEditorWidget::deleteSelectedFeatures()
492493

493494
void QgsRelationEditorWidget::deleteFeatures( const QgsFeatureIds &featureids )
494495
{
495-
QgsVectorLayer *layer = mNmRelation.isValid() ? mNmRelation.referencedLayer() : mRelation.referencingLayer();
496-
layer->deleteFeatures( featureids );
497-
updateUi();
496+
bool deleteFeatures = true;
497+
498+
QgsVectorLayer *layer;
499+
if ( mNmRelation.isValid() )
500+
{
501+
layer = mNmRelation.referencedLayer();
502+
503+
// When deleting a linked feature within an N:M relation,
504+
// check if the feature is linked to more than just one feature.
505+
// In case it is linked more than just once, ask the user for confirmation
506+
// as it is likely he was not aware of the implications and might either
507+
// leave the dataset in a corrupted state (referential integrity) or if
508+
// the fk constraint is ON CASCADE DELETE, there may be several linking
509+
// entries deleted along.
510+
511+
QgsFeatureRequest deletedFeaturesRequest;
512+
deletedFeaturesRequest.setFilterFids( featureids );
513+
deletedFeaturesRequest.setFlags( QgsFeatureRequest::NoGeometry );
514+
deletedFeaturesRequest.setSubsetOfAttributes( QgsAttributeList() << mNmRelation.referencedFields().first() );
515+
516+
QgsFeatureIterator deletedFeatures = layer->getFeatures( deletedFeaturesRequest );
517+
QStringList deletedFeaturesPks;
518+
QgsFeature feature;
519+
while ( deletedFeatures.nextFeature( feature ) )
520+
{
521+
deletedFeaturesPks.append( QgsExpression::quotedValue( feature.attribute( mNmRelation.referencedFields().first() ) ) );
522+
}
523+
524+
QgsFeatureRequest linkingFeaturesRequest;
525+
linkingFeaturesRequest.setFlags( QgsFeatureRequest::NoGeometry );
526+
linkingFeaturesRequest.setSubsetOfAttributes( QgsAttributeList() );
527+
528+
QString linkingFeaturesRequestExpression;
529+
if ( !deletedFeaturesPks.empty() )
530+
{
531+
linkingFeaturesRequestExpression = QStringLiteral( "%1 IN (%2)" ).arg( QgsExpression::quotedColumnRef( mNmRelation.fieldPairs().first().first ), deletedFeaturesPks.join( ',' ) );
532+
linkingFeaturesRequest.setFilterExpression( linkingFeaturesRequestExpression );
533+
534+
QgsFeatureIterator relatedLinkingFeatures = mNmRelation.referencingLayer()->getFeatures( linkingFeaturesRequest );
535+
536+
int relatedLinkingFeaturesCount = 0;
537+
while ( relatedLinkingFeatures.nextFeature( feature ) )
538+
{
539+
relatedLinkingFeaturesCount++;
540+
}
541+
542+
if ( deletedFeaturesPks.size() == 1 && relatedLinkingFeaturesCount > 1 )
543+
{
544+
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 );
545+
messageBox.addButton( QMessageBox::Cancel );
546+
QAbstractButton *deleteButton = messageBox.addButton( tr( "Delete" ), QMessageBox::AcceptRole );
547+
548+
messageBox.exec();
549+
if ( messageBox.clickedButton() != deleteButton )
550+
deleteFeatures = false;
551+
}
552+
else if ( deletedFeaturesPks.size() > 1 && relatedLinkingFeaturesCount > deletedFeaturesPks.size() )
553+
{
554+
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 );
555+
messageBox.addButton( QMessageBox::Cancel );
556+
QAbstractButton *deleteButton = messageBox.addButton( tr( "Delete" ), QMessageBox::AcceptRole );
557+
558+
messageBox.exec();
559+
if ( messageBox.clickedButton() != deleteButton )
560+
deleteFeatures = false;
561+
}
562+
}
563+
}
564+
else
565+
{
566+
layer = mRelation.referencingLayer();
567+
}
568+
569+
if ( deleteFeatures )
570+
{
571+
layer->deleteFeatures( featureids );
572+
updateUi();
573+
}
498574
}
499575

500576
void QgsRelationEditorWidget::unlinkFeature( const QgsFeatureId featureid )

‎tests/src/python/test_qgsrelationeditwidget.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@
3434
)
3535

3636
from qgis.PyQt.QtCore import QTimer
37-
from qgis.PyQt.QtWidgets import QToolButton, QTableView, QApplication
37+
from qgis.PyQt.QtWidgets import (
38+
QToolButton,
39+
QMessageBox,
40+
QDialogButtonBox,
41+
QTableView,
42+
QApplication
43+
)
3844
from qgis.testing import start_app, unittest
3945

4046
start_app()
@@ -92,6 +98,7 @@ def setUp(self):
9298

9399
def tearDown(self):
94100
self.rollbackTransaction()
101+
del self.transaction
95102

96103
def test_delete_feature(self):
97104
"""
@@ -108,6 +115,16 @@ def test_delete_feature(self):
108115
self.widget.featureSelectionManager().select([fid])
109116

110117
btn = self.widget.findChild(QToolButton, 'mDeleteFeatureButton')
118+
119+
def clickOk():
120+
# Click the "Delete features" button on the confirmation message
121+
# box
122+
widget = self.widget.findChild(QMessageBox)
123+
buttonBox = widget.findChild(QDialogButtonBox)
124+
deleteButton = next((b for b in buttonBox.buttons() if buttonBox.buttonRole(b) == QDialogButtonBox.AcceptRole))
125+
deleteButton.click()
126+
127+
QTimer.singleShot(1, clickOk)
111128
btn.click()
112129

113130
# This is the important check that the feature is deleted

0 commit comments

Comments
 (0)
Please sign in to comment.