Skip to content

Commit

Permalink
[FEATURE] Field constraints can be enforced or not
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nyalldawson committed Nov 2, 2016
1 parent e3a6083 commit fac5bc0
Show file tree
Hide file tree
Showing 15 changed files with 406 additions and 76 deletions.
21 changes: 19 additions & 2 deletions python/core/qgsfieldconstraints.sip
Expand Up @@ -41,8 +41,9 @@ class QgsFieldConstraints
*/
enum ConstraintStrength
{
ConstraintHard, //!< Constraint must be honored before feature can be accepted
ConstraintSoft, //!< User is warned if constraint is violated but feature can still be accepted
ConstraintStrengthNotSet, //!< Constraint is not set
ConstraintStrengthHard, //!< Constraint must be honored before feature can be accepted
ConstraintStrengthSoft, //!< User is warned if constraint is violated but feature can still be accepted
};

/**
Expand All @@ -64,6 +65,22 @@ class QgsFieldConstraints
*/
ConstraintOrigin constraintOrigin( Constraint constraint ) const;

/**
* Returns the strength of a field constraint, or ConstraintStrengthNotSet if the constraint
* is not present on this field.
* @see constraints()
* @see setConstraintStrength()
*/
ConstraintStrength constraintStrength( Constraint constraint ) const;

/**
* Sets the strength of a constraint. Note that the strength of constraints which originate
* from a provider cannot be changed. Constraints default to ConstraintStrengthHard unless
* explicitly changed.
* @see constraintStrength()
*/
void setConstraintStrength( Constraint constraint, ConstraintStrength strength );

/**
* Sets a constraint on the field.
* @see constraints()
Expand Down
18 changes: 14 additions & 4 deletions python/core/qgsvectorlayer.sip
Expand Up @@ -1262,18 +1262,28 @@ class QgsVectorLayer : QgsMapLayer
* field index. These constraints may be inherited from the layer's data provider
* or may be set manually on the vector layer from within QGIS.
* @note added in QGIS 3.0
* @see setFieldConstraints()
* @see setFieldConstraint()
*/
QgsFieldConstraints::Constraints fieldConstraints( int fieldIndex ) const;

/**
* Sets the constraints for a specified field index. Any constraints inherited from the layer's
* data provider will be kept intact and cannot be cleared. Ie, calling this method only allows for new
* Sets a constraint for a specified field index. Any constraints inherited from the layer's
* data provider will be kept intact and cannot be modified. Ie, calling this method only allows for new
* constraints to be added on top of the existing provider constraints.
* @note added in QGIS 3.0
* @see fieldConstraints()
* @see removeFieldConstraint()
*/
void setFieldConstraints( int index, QgsFieldConstraints::Constraints constraints );
void setFieldConstraint( int index, QgsFieldConstraints::Constraint constraint, QgsFieldConstraints::ConstraintStrength strength = QgsFieldConstraints::ConstraintStrengthHard );

/**
* Removes a constraint for a specified field index. Any constraints inherited from the layer's
* data provider will be kept intact and cannot be removed.
* @note added in QGIS 3.0
* @see fieldConstraints()
* @see setFieldConstraint()
*/
void removeFieldConstraint( int index, QgsFieldConstraints::Constraint constraint );

/**
* Returns the constraint expression for for a specified field index, if set.
Expand Down
52 changes: 50 additions & 2 deletions src/app/qgsattributetypedialog.cpp
Expand Up @@ -69,7 +69,21 @@ QgsAttributeTypeDialog::QgsAttributeTypeDialog( QgsVectorLayer *vl, int fieldIdx
isFieldEditableCheckBox->setEnabled( false );
}

connect( mExpressionWidget, SIGNAL( expressionChanged( QString ) ), this, SLOT( defaultExpressionChanged() ) );
connect( mExpressionWidget, &QgsExpressionLineEdit::expressionChanged, this, &QgsAttributeTypeDialog::defaultExpressionChanged );
connect( mUniqueCheckBox, &QCheckBox::toggled, this, [=]( bool checked )
{
mCheckBoxEnforceUnique->setEnabled( checked );
if ( !checked )
mCheckBoxEnforceUnique->setChecked( false );
}
);
connect( notNullCheckBox, &QCheckBox::toggled, this, [=]( bool checked )
{
mCheckBoxEnforceNotNull->setEnabled( checked );
if ( !checked )
mCheckBoxEnforceNotNull->setChecked( false );
}
);

QSettings settings;
restoreGeometry( settings.value( QStringLiteral( "/Windows/QgsAttributeTypeDialog/geometry" ) ).toByteArray() );
Expand Down Expand Up @@ -154,7 +168,7 @@ void QgsAttributeTypeDialog::setWidgetType( const QString& type )
stackedWidget->addWidget( cfgWdg );
stackedWidget->setCurrentWidget( cfgWdg );
mEditorConfigWidgets.insert( type, cfgWdg );
connect( cfgWdg, SIGNAL( changed() ), this, SLOT( defaultExpressionChanged() ) );
connect( cfgWdg, &QgsEditorConfigWidget::changed, this, &QgsAttributeTypeDialog::defaultExpressionChanged );
}
else
{
Expand Down Expand Up @@ -183,13 +197,17 @@ void QgsAttributeTypeDialog::setProviderConstraints( QgsFieldConstraints::Constr
notNullCheckBox->setChecked( true );
notNullCheckBox->setEnabled( false );
notNullCheckBox->setToolTip( tr( "The provider for this layer has a NOT NULL constraint set on the field." ) );
mCheckBoxEnforceNotNull->setChecked( true );
mCheckBoxEnforceNotNull->setEnabled( false );
}

if ( constraints & QgsFieldConstraints::ConstraintUnique )
{
mUniqueCheckBox->setChecked( true );
mUniqueCheckBox->setEnabled( false );
mUniqueCheckBox->setToolTip( tr( "The provider for this layer has a UNIQUE constraint set on the field." ) );
mCheckBoxEnforceUnique->setChecked( true );
mCheckBoxEnforceUnique->setEnabled( false );
}
}

Expand Down Expand Up @@ -218,6 +236,16 @@ bool QgsAttributeTypeDialog::notNull() const
return notNullCheckBox->isChecked();
}

void QgsAttributeTypeDialog::setNotNullEnforced( bool enforced )
{
mCheckBoxEnforceNotNull->setChecked( enforced );
}

bool QgsAttributeTypeDialog::notNullEnforced() const
{
return mCheckBoxEnforceNotNull->isChecked();
}

void QgsAttributeTypeDialog::setUnique( bool unique )
{
mUniqueCheckBox->setChecked( unique );
Expand All @@ -228,11 +256,31 @@ bool QgsAttributeTypeDialog::unique() const
return mUniqueCheckBox->isChecked();
}

void QgsAttributeTypeDialog::setUniqueEnforced( bool enforced )
{
mCheckBoxEnforceUnique->setChecked( enforced );
}

bool QgsAttributeTypeDialog::uniqueEnforced() const
{
return mCheckBoxEnforceUnique->isChecked();
}

void QgsAttributeTypeDialog::setConstraintExpression( const QString &str )
{
constraintExpressionWidget->setField( str );
}

void QgsAttributeTypeDialog::setConstraintExpressionEnforced( bool enforced )
{
mCheckBoxEnforceExpression->setChecked( enforced );
}

bool QgsAttributeTypeDialog::constraintExpressionEnforced() const
{
return mCheckBoxEnforceExpression->isChecked();
}

QString QgsAttributeTypeDialog::defaultValueExpression() const
{
return mExpressionWidget->expression();
Expand Down
30 changes: 30 additions & 0 deletions src/app/qgsattributetypedialog.h
Expand Up @@ -85,6 +85,16 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
*/
bool notNull() const;

/**
* Sets whether the not null constraint is enforced.
*/
void setNotNullEnforced( bool enforced );

/**
* Returns whether the not null constraint should be enforced.
*/
bool notNullEnforced() const;

/**
* Setter for unique constraint checkbox
*/
Expand All @@ -95,6 +105,16 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
*/
bool unique() const;

/**
* Sets whether the not null constraint is enforced.
*/
void setUniqueEnforced( bool enforced );

/**
* Returns whether the not null constraint should be enforced.
*/
bool uniqueEnforced() const;

/**
* Setter for constraint expression description
* @param desc the expression description
Expand All @@ -121,6 +141,16 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
*/
void setConstraintExpression( const QString &str );

/**
* Sets whether the expression constraint is enforced.
*/
void setConstraintExpressionEnforced( bool enforced );

/**
* Returns whether the expression constraint should be enforced.
*/
bool constraintExpressionEnforced() const;

/**
* Returns the expression used for the field's default value, or
* an empty string if no default value expression is set.
Expand Down
30 changes: 29 additions & 1 deletion src/app/qgsfieldsproperties.cpp
Expand Up @@ -559,7 +559,9 @@ void QgsFieldsProperties::attributeTypeDialog()
attributeTypeDialog.setFieldEditable( cfg.mEditable );
attributeTypeDialog.setLabelOnTop( cfg.mLabelOnTop );
attributeTypeDialog.setNotNull( cfg.mConstraints & QgsFieldConstraints::ConstraintNotNull );
attributeTypeDialog.setNotNullEnforced( cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintStrengthHard ) == QgsFieldConstraints::ConstraintStrengthHard );
attributeTypeDialog.setUnique( cfg.mConstraints & QgsFieldConstraints::ConstraintUnique );
attributeTypeDialog.setUniqueEnforced( cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintStrengthHard ) == QgsFieldConstraints::ConstraintStrengthHard );

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

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

attributeTypeDialog.setWidgetConfig( cfg.mEditorWidgetConfig );
Expand All @@ -593,6 +596,10 @@ void QgsFieldsProperties::attributeTypeDialog()
{
cfg.mConstraints |= QgsFieldConstraints::ConstraintUnique;
}
if ( !attributeTypeDialog.constraintExpression().isEmpty() && !( providerConstraints & QgsFieldConstraints::ConstraintExpression ) )
{
cfg.mConstraints |= QgsFieldConstraints::ConstraintExpression;
}

cfg.mConstraintDescription = attributeTypeDialog.constraintExpressionDescription();
cfg.mConstraint = attributeTypeDialog.constraintExpression();
Expand All @@ -601,6 +608,13 @@ void QgsFieldsProperties::attributeTypeDialog()
cfg.mEditorWidgetType = attributeTypeDialog.editorWidgetType();
cfg.mEditorWidgetConfig = attributeTypeDialog.editorWidgetConfig();

cfg.mConstraintStrength.insert( QgsFieldConstraints::ConstraintNotNull, attributeTypeDialog.notNullEnforced() ?
QgsFieldConstraints::ConstraintStrengthHard : QgsFieldConstraints::ConstraintStrengthSoft );
cfg.mConstraintStrength.insert( QgsFieldConstraints::ConstraintUnique, attributeTypeDialog.uniqueEnforced() ?
QgsFieldConstraints::ConstraintStrengthHard : QgsFieldConstraints::ConstraintStrengthSoft );
cfg.mConstraintStrength.insert( QgsFieldConstraints::ConstraintExpression, attributeTypeDialog.constraintExpressionEnforced() ?
QgsFieldConstraints::ConstraintStrengthHard : QgsFieldConstraints::ConstraintStrengthSoft );

pb->setText( attributeTypeDialog.editorWidgetText() );

setConfigForRow( row, cfg );
Expand Down Expand Up @@ -983,7 +997,18 @@ void QgsFieldsProperties::apply()
editFormConfig.setWidgetType( name, cfg.mEditorWidgetType );
editFormConfig.setWidgetConfig( name, cfg.mEditorWidgetConfig );

mLayer->setFieldConstraints( i, cfg.mConstraints );
if ( cfg.mConstraints & QgsFieldConstraints::ConstraintNotNull )
{
mLayer->setFieldConstraint( i, QgsFieldConstraints::ConstraintNotNull, cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintStrengthHard ) );
}
if ( cfg.mConstraints & QgsFieldConstraints::ConstraintUnique )
{
mLayer->setFieldConstraint( i, QgsFieldConstraints::ConstraintUnique, cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintStrengthHard ) );
}
if ( cfg.mConstraints & QgsFieldConstraints::ConstraintExpression )
{
mLayer->setFieldConstraint( i, QgsFieldConstraints::ConstraintExpression, cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintExpression, QgsFieldConstraints::ConstraintStrengthHard ) );
}

if ( mFieldsList->item( i, attrWMSCol )->checkState() == Qt::Unchecked )
{
Expand Down Expand Up @@ -1064,6 +1089,9 @@ QgsFieldsProperties::FieldConfig::FieldConfig( QgsVectorLayer* layer, int idx )
QgsFieldConstraints constraints = layer->fields().at( idx ).constraints();
mConstraints = constraints.constraints();
mConstraint = constraints.constraintExpression();
mConstraintStrength.insert( QgsFieldConstraints::ConstraintNotNull, constraints.constraintStrength( QgsFieldConstraints::ConstraintNotNull ) );
mConstraintStrength.insert( QgsFieldConstraints::ConstraintUnique, constraints.constraintStrength( QgsFieldConstraints::ConstraintUnique ) );
mConstraintStrength.insert( QgsFieldConstraints::ConstraintExpression, constraints.constraintStrength( QgsFieldConstraints::ConstraintExpression ) );
mConstraintDescription = constraints.constraintDescription();
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( layer, layer->fields().field( idx ).name() );
mEditorWidgetType = setup.type();
Expand Down
1 change: 1 addition & 0 deletions src/app/qgsfieldsproperties.h
Expand Up @@ -123,6 +123,7 @@ class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPrope
bool mEditableEnabled;
bool mLabelOnTop;
QgsFieldConstraints::Constraints mConstraints;
QHash< QgsFieldConstraints::Constraint, QgsFieldConstraints::ConstraintStrength > mConstraintStrength;
QString mConstraint;
QString mConstraintDescription;
QPushButton* mButton;
Expand Down
16 changes: 14 additions & 2 deletions src/core/qgsfield.cpp
Expand Up @@ -317,6 +317,9 @@ QDataStream& operator<<( QDataStream& out, const QgsField& field )
out << static_cast< quint32 >( field.constraints().constraintOrigin( QgsFieldConstraints::ConstraintNotNull ) );
out << static_cast< quint32 >( field.constraints().constraintOrigin( QgsFieldConstraints::ConstraintUnique ) );
out << static_cast< quint32 >( field.constraints().constraintOrigin( QgsFieldConstraints::ConstraintExpression ) );
out << static_cast< quint32 >( field.constraints().constraintStrength( QgsFieldConstraints::ConstraintNotNull ) );
out << static_cast< quint32 >( field.constraints().constraintStrength( QgsFieldConstraints::ConstraintUnique ) );
out << static_cast< quint32 >( field.constraints().constraintStrength( QgsFieldConstraints::ConstraintExpression ) );
out << field.constraints().constraintExpression();
out << field.constraints().constraintDescription();
out << static_cast< quint32 >( field.subType() );
Expand All @@ -325,10 +328,10 @@ QDataStream& operator<<( QDataStream& out, const QgsField& field )

QDataStream& operator>>( QDataStream& in, QgsField& field )
{
quint32 type, subType, length, precision, constraints, originNotNull, originUnique, originExpression;
quint32 type, subType, length, precision, constraints, originNotNull, originUnique, originExpression, strengthNotNull, strengthUnique, strengthExpression;
QString name, typeName, comment, alias, defaultValueExpression, constraintExpression, constraintDescription;
in >> name >> type >> typeName >> length >> precision >> comment >> alias
>> defaultValueExpression >> constraints >> originNotNull >> originUnique >> originExpression >>
>> defaultValueExpression >> constraints >> originNotNull >> originUnique >> originExpression >> strengthNotNull >> strengthUnique >> strengthExpression >>
constraintExpression >> constraintDescription >> subType;
field.setName( name );
field.setType( static_cast< QVariant::Type >( type ) );
Expand All @@ -340,15 +343,24 @@ QDataStream& operator>>( QDataStream& in, QgsField& field )
field.setDefaultValueExpression( defaultValueExpression );
QgsFieldConstraints fieldConstraints;
if ( constraints & QgsFieldConstraints::ConstraintNotNull )
{
fieldConstraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, static_cast< QgsFieldConstraints::ConstraintOrigin>( originNotNull ) );
fieldConstraints.setConstraintStrength( QgsFieldConstraints::ConstraintNotNull, static_cast< QgsFieldConstraints::ConstraintStrength>( strengthNotNull ) );
}
else
fieldConstraints.removeConstraint( QgsFieldConstraints::ConstraintNotNull );
if ( constraints & QgsFieldConstraints::ConstraintUnique )
{
fieldConstraints.setConstraint( QgsFieldConstraints::ConstraintUnique, static_cast< QgsFieldConstraints::ConstraintOrigin>( originUnique ) );
fieldConstraints.setConstraintStrength( QgsFieldConstraints::ConstraintUnique, static_cast< QgsFieldConstraints::ConstraintStrength>( strengthUnique ) );
}
else
fieldConstraints.removeConstraint( QgsFieldConstraints::ConstraintUnique );
if ( constraints & QgsFieldConstraints::ConstraintExpression )
{
fieldConstraints.setConstraint( QgsFieldConstraints::ConstraintExpression, static_cast< QgsFieldConstraints::ConstraintOrigin>( originExpression ) );
fieldConstraints.setConstraintStrength( QgsFieldConstraints::ConstraintExpression, static_cast< QgsFieldConstraints::ConstraintStrength>( strengthExpression ) );
}
else
fieldConstraints.removeConstraint( QgsFieldConstraints::ConstraintExpression );
fieldConstraints.setConstraintExpression( constraintExpression, constraintDescription );
Expand Down

0 comments on commit fac5bc0

Please sign in to comment.