Skip to content

Commit fac5bc0

Browse files
committed
[FEATURE] Field constraints can be enforced or not
Non-enforced constraints just show a warning to the user, but do not prevent committing the feature. Enforced constraints block users from comitting non compliant features. Any constraints detected by the provider are always enforced.
1 parent e3a6083 commit fac5bc0

15 files changed

+406
-76
lines changed

python/core/qgsfieldconstraints.sip

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ class QgsFieldConstraints
4141
*/
4242
enum ConstraintStrength
4343
{
44-
ConstraintHard, //!< Constraint must be honored before feature can be accepted
45-
ConstraintSoft, //!< User is warned if constraint is violated but feature can still be accepted
44+
ConstraintStrengthNotSet, //!< Constraint is not set
45+
ConstraintStrengthHard, //!< Constraint must be honored before feature can be accepted
46+
ConstraintStrengthSoft, //!< User is warned if constraint is violated but feature can still be accepted
4647
};
4748

4849
/**
@@ -64,6 +65,22 @@ class QgsFieldConstraints
6465
*/
6566
ConstraintOrigin constraintOrigin( Constraint constraint ) const;
6667

68+
/**
69+
* Returns the strength of a field constraint, or ConstraintStrengthNotSet if the constraint
70+
* is not present on this field.
71+
* @see constraints()
72+
* @see setConstraintStrength()
73+
*/
74+
ConstraintStrength constraintStrength( Constraint constraint ) const;
75+
76+
/**
77+
* Sets the strength of a constraint. Note that the strength of constraints which originate
78+
* from a provider cannot be changed. Constraints default to ConstraintStrengthHard unless
79+
* explicitly changed.
80+
* @see constraintStrength()
81+
*/
82+
void setConstraintStrength( Constraint constraint, ConstraintStrength strength );
83+
6784
/**
6885
* Sets a constraint on the field.
6986
* @see constraints()

python/core/qgsvectorlayer.sip

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,18 +1262,28 @@ class QgsVectorLayer : QgsMapLayer
12621262
* field index. These constraints may be inherited from the layer's data provider
12631263
* or may be set manually on the vector layer from within QGIS.
12641264
* @note added in QGIS 3.0
1265-
* @see setFieldConstraints()
1265+
* @see setFieldConstraint()
12661266
*/
12671267
QgsFieldConstraints::Constraints fieldConstraints( int fieldIndex ) const;
12681268

12691269
/**
1270-
* Sets the constraints for a specified field index. Any constraints inherited from the layer's
1271-
* data provider will be kept intact and cannot be cleared. Ie, calling this method only allows for new
1270+
* Sets a constraint for a specified field index. Any constraints inherited from the layer's
1271+
* data provider will be kept intact and cannot be modified. Ie, calling this method only allows for new
12721272
* constraints to be added on top of the existing provider constraints.
12731273
* @note added in QGIS 3.0
12741274
* @see fieldConstraints()
1275+
* @see removeFieldConstraint()
12751276
*/
1276-
void setFieldConstraints( int index, QgsFieldConstraints::Constraints constraints );
1277+
void setFieldConstraint( int index, QgsFieldConstraints::Constraint constraint, QgsFieldConstraints::ConstraintStrength strength = QgsFieldConstraints::ConstraintStrengthHard );
1278+
1279+
/**
1280+
* Removes a constraint for a specified field index. Any constraints inherited from the layer's
1281+
* data provider will be kept intact and cannot be removed.
1282+
* @note added in QGIS 3.0
1283+
* @see fieldConstraints()
1284+
* @see setFieldConstraint()
1285+
*/
1286+
void removeFieldConstraint( int index, QgsFieldConstraints::Constraint constraint );
12771287

12781288
/**
12791289
* Returns the constraint expression for for a specified field index, if set.

src/app/qgsattributetypedialog.cpp

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,21 @@ QgsAttributeTypeDialog::QgsAttributeTypeDialog( QgsVectorLayer *vl, int fieldIdx
6969
isFieldEditableCheckBox->setEnabled( false );
7070
}
7171

72-
connect( mExpressionWidget, SIGNAL( expressionChanged( QString ) ), this, SLOT( defaultExpressionChanged() ) );
72+
connect( mExpressionWidget, &QgsExpressionLineEdit::expressionChanged, this, &QgsAttributeTypeDialog::defaultExpressionChanged );
73+
connect( mUniqueCheckBox, &QCheckBox::toggled, this, [=]( bool checked )
74+
{
75+
mCheckBoxEnforceUnique->setEnabled( checked );
76+
if ( !checked )
77+
mCheckBoxEnforceUnique->setChecked( false );
78+
}
79+
);
80+
connect( notNullCheckBox, &QCheckBox::toggled, this, [=]( bool checked )
81+
{
82+
mCheckBoxEnforceNotNull->setEnabled( checked );
83+
if ( !checked )
84+
mCheckBoxEnforceNotNull->setChecked( false );
85+
}
86+
);
7387

7488
QSettings settings;
7589
restoreGeometry( settings.value( QStringLiteral( "/Windows/QgsAttributeTypeDialog/geometry" ) ).toByteArray() );
@@ -154,7 +168,7 @@ void QgsAttributeTypeDialog::setWidgetType( const QString& type )
154168
stackedWidget->addWidget( cfgWdg );
155169
stackedWidget->setCurrentWidget( cfgWdg );
156170
mEditorConfigWidgets.insert( type, cfgWdg );
157-
connect( cfgWdg, SIGNAL( changed() ), this, SLOT( defaultExpressionChanged() ) );
171+
connect( cfgWdg, &QgsEditorConfigWidget::changed, this, &QgsAttributeTypeDialog::defaultExpressionChanged );
158172
}
159173
else
160174
{
@@ -183,13 +197,17 @@ void QgsAttributeTypeDialog::setProviderConstraints( QgsFieldConstraints::Constr
183197
notNullCheckBox->setChecked( true );
184198
notNullCheckBox->setEnabled( false );
185199
notNullCheckBox->setToolTip( tr( "The provider for this layer has a NOT NULL constraint set on the field." ) );
200+
mCheckBoxEnforceNotNull->setChecked( true );
201+
mCheckBoxEnforceNotNull->setEnabled( false );
186202
}
187203

188204
if ( constraints & QgsFieldConstraints::ConstraintUnique )
189205
{
190206
mUniqueCheckBox->setChecked( true );
191207
mUniqueCheckBox->setEnabled( false );
192208
mUniqueCheckBox->setToolTip( tr( "The provider for this layer has a UNIQUE constraint set on the field." ) );
209+
mCheckBoxEnforceUnique->setChecked( true );
210+
mCheckBoxEnforceUnique->setEnabled( false );
193211
}
194212
}
195213

@@ -218,6 +236,16 @@ bool QgsAttributeTypeDialog::notNull() const
218236
return notNullCheckBox->isChecked();
219237
}
220238

239+
void QgsAttributeTypeDialog::setNotNullEnforced( bool enforced )
240+
{
241+
mCheckBoxEnforceNotNull->setChecked( enforced );
242+
}
243+
244+
bool QgsAttributeTypeDialog::notNullEnforced() const
245+
{
246+
return mCheckBoxEnforceNotNull->isChecked();
247+
}
248+
221249
void QgsAttributeTypeDialog::setUnique( bool unique )
222250
{
223251
mUniqueCheckBox->setChecked( unique );
@@ -228,11 +256,31 @@ bool QgsAttributeTypeDialog::unique() const
228256
return mUniqueCheckBox->isChecked();
229257
}
230258

259+
void QgsAttributeTypeDialog::setUniqueEnforced( bool enforced )
260+
{
261+
mCheckBoxEnforceUnique->setChecked( enforced );
262+
}
263+
264+
bool QgsAttributeTypeDialog::uniqueEnforced() const
265+
{
266+
return mCheckBoxEnforceUnique->isChecked();
267+
}
268+
231269
void QgsAttributeTypeDialog::setConstraintExpression( const QString &str )
232270
{
233271
constraintExpressionWidget->setField( str );
234272
}
235273

274+
void QgsAttributeTypeDialog::setConstraintExpressionEnforced( bool enforced )
275+
{
276+
mCheckBoxEnforceExpression->setChecked( enforced );
277+
}
278+
279+
bool QgsAttributeTypeDialog::constraintExpressionEnforced() const
280+
{
281+
return mCheckBoxEnforceExpression->isChecked();
282+
}
283+
236284
QString QgsAttributeTypeDialog::defaultValueExpression() const
237285
{
238286
return mExpressionWidget->expression();

src/app/qgsattributetypedialog.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,16 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
8585
*/
8686
bool notNull() const;
8787

88+
/**
89+
* Sets whether the not null constraint is enforced.
90+
*/
91+
void setNotNullEnforced( bool enforced );
92+
93+
/**
94+
* Returns whether the not null constraint should be enforced.
95+
*/
96+
bool notNullEnforced() const;
97+
8898
/**
8999
* Setter for unique constraint checkbox
90100
*/
@@ -95,6 +105,16 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
95105
*/
96106
bool unique() const;
97107

108+
/**
109+
* Sets whether the not null constraint is enforced.
110+
*/
111+
void setUniqueEnforced( bool enforced );
112+
113+
/**
114+
* Returns whether the not null constraint should be enforced.
115+
*/
116+
bool uniqueEnforced() const;
117+
98118
/**
99119
* Setter for constraint expression description
100120
* @param desc the expression description
@@ -121,6 +141,16 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
121141
*/
122142
void setConstraintExpression( const QString &str );
123143

144+
/**
145+
* Sets whether the expression constraint is enforced.
146+
*/
147+
void setConstraintExpressionEnforced( bool enforced );
148+
149+
/**
150+
* Returns whether the expression constraint should be enforced.
151+
*/
152+
bool constraintExpressionEnforced() const;
153+
124154
/**
125155
* Returns the expression used for the field's default value, or
126156
* an empty string if no default value expression is set.

src/app/qgsfieldsproperties.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,9 @@ void QgsFieldsProperties::attributeTypeDialog()
559559
attributeTypeDialog.setFieldEditable( cfg.mEditable );
560560
attributeTypeDialog.setLabelOnTop( cfg.mLabelOnTop );
561561
attributeTypeDialog.setNotNull( cfg.mConstraints & QgsFieldConstraints::ConstraintNotNull );
562+
attributeTypeDialog.setNotNullEnforced( cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintStrengthHard ) == QgsFieldConstraints::ConstraintStrengthHard );
562563
attributeTypeDialog.setUnique( cfg.mConstraints & QgsFieldConstraints::ConstraintUnique );
564+
attributeTypeDialog.setUniqueEnforced( cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintStrengthHard ) == QgsFieldConstraints::ConstraintStrengthHard );
563565

564566
QgsFieldConstraints constraints = mLayer->fields().at( index ).constraints();
565567
QgsFieldConstraints::Constraints providerConstraints = 0;
@@ -573,6 +575,7 @@ void QgsFieldsProperties::attributeTypeDialog()
573575

574576
attributeTypeDialog.setConstraintExpression( cfg.mConstraint );
575577
attributeTypeDialog.setConstraintExpressionDescription( cfg.mConstraintDescription );
578+
attributeTypeDialog.setConstraintExpressionEnforced( cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintExpression, QgsFieldConstraints::ConstraintStrengthHard ) == QgsFieldConstraints::ConstraintStrengthHard );
576579
attributeTypeDialog.setDefaultValueExpression( mLayer->defaultValueExpression( index ) );
577580

578581
attributeTypeDialog.setWidgetConfig( cfg.mEditorWidgetConfig );
@@ -593,6 +596,10 @@ void QgsFieldsProperties::attributeTypeDialog()
593596
{
594597
cfg.mConstraints |= QgsFieldConstraints::ConstraintUnique;
595598
}
599+
if ( !attributeTypeDialog.constraintExpression().isEmpty() && !( providerConstraints & QgsFieldConstraints::ConstraintExpression ) )
600+
{
601+
cfg.mConstraints |= QgsFieldConstraints::ConstraintExpression;
602+
}
596603

597604
cfg.mConstraintDescription = attributeTypeDialog.constraintExpressionDescription();
598605
cfg.mConstraint = attributeTypeDialog.constraintExpression();
@@ -601,6 +608,13 @@ void QgsFieldsProperties::attributeTypeDialog()
601608
cfg.mEditorWidgetType = attributeTypeDialog.editorWidgetType();
602609
cfg.mEditorWidgetConfig = attributeTypeDialog.editorWidgetConfig();
603610

611+
cfg.mConstraintStrength.insert( QgsFieldConstraints::ConstraintNotNull, attributeTypeDialog.notNullEnforced() ?
612+
QgsFieldConstraints::ConstraintStrengthHard : QgsFieldConstraints::ConstraintStrengthSoft );
613+
cfg.mConstraintStrength.insert( QgsFieldConstraints::ConstraintUnique, attributeTypeDialog.uniqueEnforced() ?
614+
QgsFieldConstraints::ConstraintStrengthHard : QgsFieldConstraints::ConstraintStrengthSoft );
615+
cfg.mConstraintStrength.insert( QgsFieldConstraints::ConstraintExpression, attributeTypeDialog.constraintExpressionEnforced() ?
616+
QgsFieldConstraints::ConstraintStrengthHard : QgsFieldConstraints::ConstraintStrengthSoft );
617+
604618
pb->setText( attributeTypeDialog.editorWidgetText() );
605619

606620
setConfigForRow( row, cfg );
@@ -983,7 +997,18 @@ void QgsFieldsProperties::apply()
983997
editFormConfig.setWidgetType( name, cfg.mEditorWidgetType );
984998
editFormConfig.setWidgetConfig( name, cfg.mEditorWidgetConfig );
985999

986-
mLayer->setFieldConstraints( i, cfg.mConstraints );
1000+
if ( cfg.mConstraints & QgsFieldConstraints::ConstraintNotNull )
1001+
{
1002+
mLayer->setFieldConstraint( i, QgsFieldConstraints::ConstraintNotNull, cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintStrengthHard ) );
1003+
}
1004+
if ( cfg.mConstraints & QgsFieldConstraints::ConstraintUnique )
1005+
{
1006+
mLayer->setFieldConstraint( i, QgsFieldConstraints::ConstraintUnique, cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintStrengthHard ) );
1007+
}
1008+
if ( cfg.mConstraints & QgsFieldConstraints::ConstraintExpression )
1009+
{
1010+
mLayer->setFieldConstraint( i, QgsFieldConstraints::ConstraintExpression, cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintExpression, QgsFieldConstraints::ConstraintStrengthHard ) );
1011+
}
9871012

9881013
if ( mFieldsList->item( i, attrWMSCol )->checkState() == Qt::Unchecked )
9891014
{
@@ -1064,6 +1089,9 @@ QgsFieldsProperties::FieldConfig::FieldConfig( QgsVectorLayer* layer, int idx )
10641089
QgsFieldConstraints constraints = layer->fields().at( idx ).constraints();
10651090
mConstraints = constraints.constraints();
10661091
mConstraint = constraints.constraintExpression();
1092+
mConstraintStrength.insert( QgsFieldConstraints::ConstraintNotNull, constraints.constraintStrength( QgsFieldConstraints::ConstraintNotNull ) );
1093+
mConstraintStrength.insert( QgsFieldConstraints::ConstraintUnique, constraints.constraintStrength( QgsFieldConstraints::ConstraintUnique ) );
1094+
mConstraintStrength.insert( QgsFieldConstraints::ConstraintExpression, constraints.constraintStrength( QgsFieldConstraints::ConstraintExpression ) );
10671095
mConstraintDescription = constraints.constraintDescription();
10681096
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( layer, layer->fields().field( idx ).name() );
10691097
mEditorWidgetType = setup.type();

src/app/qgsfieldsproperties.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPrope
123123
bool mEditableEnabled;
124124
bool mLabelOnTop;
125125
QgsFieldConstraints::Constraints mConstraints;
126+
QHash< QgsFieldConstraints::Constraint, QgsFieldConstraints::ConstraintStrength > mConstraintStrength;
126127
QString mConstraint;
127128
QString mConstraintDescription;
128129
QPushButton* mButton;

src/core/qgsfield.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@ QDataStream& operator<<( QDataStream& out, const QgsField& field )
317317
out << static_cast< quint32 >( field.constraints().constraintOrigin( QgsFieldConstraints::ConstraintNotNull ) );
318318
out << static_cast< quint32 >( field.constraints().constraintOrigin( QgsFieldConstraints::ConstraintUnique ) );
319319
out << static_cast< quint32 >( field.constraints().constraintOrigin( QgsFieldConstraints::ConstraintExpression ) );
320+
out << static_cast< quint32 >( field.constraints().constraintStrength( QgsFieldConstraints::ConstraintNotNull ) );
321+
out << static_cast< quint32 >( field.constraints().constraintStrength( QgsFieldConstraints::ConstraintUnique ) );
322+
out << static_cast< quint32 >( field.constraints().constraintStrength( QgsFieldConstraints::ConstraintExpression ) );
320323
out << field.constraints().constraintExpression();
321324
out << field.constraints().constraintDescription();
322325
out << static_cast< quint32 >( field.subType() );
@@ -325,10 +328,10 @@ QDataStream& operator<<( QDataStream& out, const QgsField& field )
325328

326329
QDataStream& operator>>( QDataStream& in, QgsField& field )
327330
{
328-
quint32 type, subType, length, precision, constraints, originNotNull, originUnique, originExpression;
331+
quint32 type, subType, length, precision, constraints, originNotNull, originUnique, originExpression, strengthNotNull, strengthUnique, strengthExpression;
329332
QString name, typeName, comment, alias, defaultValueExpression, constraintExpression, constraintDescription;
330333
in >> name >> type >> typeName >> length >> precision >> comment >> alias
331-
>> defaultValueExpression >> constraints >> originNotNull >> originUnique >> originExpression >>
334+
>> defaultValueExpression >> constraints >> originNotNull >> originUnique >> originExpression >> strengthNotNull >> strengthUnique >> strengthExpression >>
332335
constraintExpression >> constraintDescription >> subType;
333336
field.setName( name );
334337
field.setType( static_cast< QVariant::Type >( type ) );
@@ -340,15 +343,24 @@ QDataStream& operator>>( QDataStream& in, QgsField& field )
340343
field.setDefaultValueExpression( defaultValueExpression );
341344
QgsFieldConstraints fieldConstraints;
342345
if ( constraints & QgsFieldConstraints::ConstraintNotNull )
346+
{
343347
fieldConstraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, static_cast< QgsFieldConstraints::ConstraintOrigin>( originNotNull ) );
348+
fieldConstraints.setConstraintStrength( QgsFieldConstraints::ConstraintNotNull, static_cast< QgsFieldConstraints::ConstraintStrength>( strengthNotNull ) );
349+
}
344350
else
345351
fieldConstraints.removeConstraint( QgsFieldConstraints::ConstraintNotNull );
346352
if ( constraints & QgsFieldConstraints::ConstraintUnique )
353+
{
347354
fieldConstraints.setConstraint( QgsFieldConstraints::ConstraintUnique, static_cast< QgsFieldConstraints::ConstraintOrigin>( originUnique ) );
355+
fieldConstraints.setConstraintStrength( QgsFieldConstraints::ConstraintUnique, static_cast< QgsFieldConstraints::ConstraintStrength>( strengthUnique ) );
356+
}
348357
else
349358
fieldConstraints.removeConstraint( QgsFieldConstraints::ConstraintUnique );
350359
if ( constraints & QgsFieldConstraints::ConstraintExpression )
360+
{
351361
fieldConstraints.setConstraint( QgsFieldConstraints::ConstraintExpression, static_cast< QgsFieldConstraints::ConstraintOrigin>( originExpression ) );
362+
fieldConstraints.setConstraintStrength( QgsFieldConstraints::ConstraintExpression, static_cast< QgsFieldConstraints::ConstraintStrength>( strengthExpression ) );
363+
}
352364
else
353365
fieldConstraints.removeConstraint( QgsFieldConstraints::ConstraintExpression );
354366
fieldConstraints.setConstraintExpression( constraintExpression, constraintDescription );

0 commit comments

Comments
 (0)