Skip to content
Permalink
Browse files

Enforce unique constraints in attribute form

  • Loading branch information
nyalldawson committed Nov 2, 2016
1 parent b7d0fd6 commit 1cecf37b40d632e55ff104b2da2fea75cc56b1da
@@ -104,12 +104,21 @@ class QgsEditorWidgetWrapper : QgsWidgetWrapper

/**
* Get the current constraint status.
* @return true if the constraint is valid or if there's not constraint,
* @return true if the constraint is valid or if there's no constraint,
* false otherwise
* @note added in QGIS 2.16
* @see constraintFailureReason()
*/
bool isValidConstraint() const;

/**
* Returns the reason why a constraint check has failed (or an empty string
* if constraint check was successful).
* @see isValidConstraint()
* @note added in QGIS 3.0
*/
QString constraintFailureReason() const;

signals:
/**
* Emit this signal, whenever the value changed.
@@ -17,6 +17,7 @@
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"
#include "qgsfields.h"
#include "qgsvectorlayerutils.h"

#include <QTableView>

@@ -108,14 +109,19 @@ void QgsEditorWidgetWrapper::updateConstraintWidgetStatus( bool constraintValid
void QgsEditorWidgetWrapper::updateConstraint( const QgsFeature &ft )
{
bool toEmit( false );
QString errStr( tr( "predicate is True" ) );
QString expression = layer()->editFormConfig().constraintExpression( mFieldIdx );
QString description;
QStringList expressions, descriptions;
QVariant value = ft.attribute( mFieldIdx );
QString fieldName = ft.fields().count() > mFieldIdx ? ft.fields().field( mFieldIdx ).name() : QString();

mConstraintFailureReason.clear();

QStringList errors;

if ( ! expression.isEmpty() )
{
description = layer()->editFormConfig().constraintDescription( mFieldIdx );
expressions << expression;
descriptions << layer()->editFormConfig().constraintDescription( mFieldIdx );

QgsExpressionContext context = layer()->createExpressionContext();
context.setFeature( ft );
@@ -125,11 +131,17 @@ void QgsEditorWidgetWrapper::updateConstraint( const QgsFeature &ft )
mValidConstraint = expr.evaluate( &context ).toBool();

if ( expr.hasParserError() )
errStr = expr.parserErrorString();
{
errors << tr( "Parser error: %1" ).arg( expr.parserErrorString() );
}
else if ( expr.hasEvalError() )
errStr = expr.evalErrorString();
{
errors << tr( "Evaluation error: %1" ).arg( expr.evalErrorString() );
}
else if ( ! mValidConstraint )
errStr = tr( "predicate is False" );
{
errors << tr( "%1 check failed" ).arg( layer()->editFormConfig().constraintDescription( mFieldIdx ) );
}

toEmit = true;
}
@@ -138,30 +150,66 @@ void QgsEditorWidgetWrapper::updateConstraint( const QgsFeature &ft )

if ( layer()->fieldConstraints( mFieldIdx ) & QgsField::ConstraintNotNull )
{
descriptions << QStringLiteral( "NotNull" );
if ( !expression.isEmpty() )
{
QString fieldName = ft.fields().field( mFieldIdx ).name();
expression = "( " + expression + " ) AND ( " + fieldName + " IS NOT NULL)";
description = "( " + description + " ) AND NotNull";
expressions << fieldName + " IS NOT NULL";
}
else
{
description = QStringLiteral( "NotNull" );
expression = QStringLiteral( "NotNull" );
expressions << QStringLiteral( "NotNull" );
}

mValidConstraint = mValidConstraint && !value.isNull();

if ( value.isNull() )
errStr = tr( "predicate is False" );
{
errors << tr( "Value is NULL" );
}

toEmit = true;
}

if ( layer()->fieldConstraints( mFieldIdx ) & QgsField::ConstraintUnique )
{
descriptions << QStringLiteral( "Unique" );
if ( !expression.isEmpty() )
{
expressions << fieldName + " IS UNIQUE";
}
else
{
expression = QStringLiteral( "Unique" );
}

bool alreadyExists = QgsVectorLayerUtils::valueExists( layer(), mFieldIdx, value, QgsFeatureIds() << ft.id() );
mValidConstraint = mValidConstraint && !alreadyExists;

if ( alreadyExists )
{
errors << tr( "Value is not unique" );
}

toEmit = true;
}

if ( toEmit )
{
QString errStr = errors.isEmpty() ? tr( "Constraint checks passed" ) : errors.join( '\n' );
mConstraintFailureReason = errors.join( ", " );
QString description;
if ( descriptions.size() > 1 )
description = "( " + descriptions.join( " ) AND ( " ) + " )";
else if ( !descriptions.isEmpty() )
description = descriptions.at( 0 );
QString expressionDesc;
if ( expressions.size() > 1 )
expressionDesc = "( " + expressions.join( " ) AND ( " ) + " )";
else if ( !expressions.isEmpty() )
expressionDesc = expressions.at( 0 );

updateConstraintWidgetStatus( mValidConstraint );
emit constraintStatusChanged( expression, description, errStr, mValidConstraint );
emit constraintStatusChanged( expressionDesc, description, errStr, mValidConstraint );
}
}

@@ -170,6 +218,11 @@ bool QgsEditorWidgetWrapper::isValidConstraint() const
return mValidConstraint;
}

QString QgsEditorWidgetWrapper::constraintFailureReason() const
{
return mConstraintFailureReason;
}

bool QgsEditorWidgetWrapper::isInTable( const QWidget* parent )
{
if ( !parent ) return false;
@@ -124,12 +124,21 @@ class GUI_EXPORT QgsEditorWidgetWrapper : public QgsWidgetWrapper

/**
* Get the current constraint status.
* @return true if the constraint is valid or if there's not constraint,
* @return true if the constraint is valid or if there's no constraint,
* false otherwise
* @note added in QGIS 2.16
* @see constraintFailureReason()
*/
bool isValidConstraint() const;

/**
* Returns the reason why a constraint check has failed (or an empty string
* if constraint check was successful).
* @see isValidConstraint()
* @note added in QGIS 3.0
*/
QString constraintFailureReason() const;

signals:
/**
* Emit this signal, whenever the value changed.
@@ -239,6 +248,9 @@ class GUI_EXPORT QgsEditorWidgetWrapper : public QgsWidgetWrapper
*/
bool mValidConstraint;

//! Contains the string explanation of why a constraint check failed
QString mConstraintFailureReason;

int mFieldIdx;
QgsFeature mFeature;
mutable QVariant mDefaultValue; // Cache default value, we don't want to retrieve different serial numbers if called repeatedly
@@ -824,8 +824,7 @@ bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields,
{
invalidFields.append( eww->field().name() );

QString desc = eww->layer()->editFormConfig().constraintDescription( eww->fieldIdx() );
descriptions.append( desc );
descriptions.append( eww->constraintFailureReason() );

valid = false; // continue to get all invalif fields
}

0 comments on commit 1cecf37

Please sign in to comment.
You can’t perform that action at this time.