mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-16 00:03:12 -04:00
Enforce unique constraints in attribute form
This commit is contained in:
parent
b7d0fd6f90
commit
1cecf37b40
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user