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.
|
* 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
|
* false otherwise
|
||||||
* @note added in QGIS 2.16
|
* @note added in QGIS 2.16
|
||||||
|
* @see constraintFailureReason()
|
||||||
*/
|
*/
|
||||||
bool isValidConstraint() const;
|
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:
|
signals:
|
||||||
/**
|
/**
|
||||||
* Emit this signal, whenever the value changed.
|
* Emit this signal, whenever the value changed.
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "qgsvectorlayer.h"
|
#include "qgsvectorlayer.h"
|
||||||
#include "qgsvectordataprovider.h"
|
#include "qgsvectordataprovider.h"
|
||||||
#include "qgsfields.h"
|
#include "qgsfields.h"
|
||||||
|
#include "qgsvectorlayerutils.h"
|
||||||
|
|
||||||
#include <QTableView>
|
#include <QTableView>
|
||||||
|
|
||||||
@ -108,14 +109,19 @@ void QgsEditorWidgetWrapper::updateConstraintWidgetStatus( bool constraintValid
|
|||||||
void QgsEditorWidgetWrapper::updateConstraint( const QgsFeature &ft )
|
void QgsEditorWidgetWrapper::updateConstraint( const QgsFeature &ft )
|
||||||
{
|
{
|
||||||
bool toEmit( false );
|
bool toEmit( false );
|
||||||
QString errStr( tr( "predicate is True" ) );
|
|
||||||
QString expression = layer()->editFormConfig().constraintExpression( mFieldIdx );
|
QString expression = layer()->editFormConfig().constraintExpression( mFieldIdx );
|
||||||
QString description;
|
QStringList expressions, descriptions;
|
||||||
QVariant value = ft.attribute( mFieldIdx );
|
QVariant value = ft.attribute( mFieldIdx );
|
||||||
|
QString fieldName = ft.fields().count() > mFieldIdx ? ft.fields().field( mFieldIdx ).name() : QString();
|
||||||
|
|
||||||
|
mConstraintFailureReason.clear();
|
||||||
|
|
||||||
|
QStringList errors;
|
||||||
|
|
||||||
if ( ! expression.isEmpty() )
|
if ( ! expression.isEmpty() )
|
||||||
{
|
{
|
||||||
description = layer()->editFormConfig().constraintDescription( mFieldIdx );
|
expressions << expression;
|
||||||
|
descriptions << layer()->editFormConfig().constraintDescription( mFieldIdx );
|
||||||
|
|
||||||
QgsExpressionContext context = layer()->createExpressionContext();
|
QgsExpressionContext context = layer()->createExpressionContext();
|
||||||
context.setFeature( ft );
|
context.setFeature( ft );
|
||||||
@ -125,11 +131,17 @@ void QgsEditorWidgetWrapper::updateConstraint( const QgsFeature &ft )
|
|||||||
mValidConstraint = expr.evaluate( &context ).toBool();
|
mValidConstraint = expr.evaluate( &context ).toBool();
|
||||||
|
|
||||||
if ( expr.hasParserError() )
|
if ( expr.hasParserError() )
|
||||||
errStr = expr.parserErrorString();
|
{
|
||||||
|
errors << tr( "Parser error: %1" ).arg( expr.parserErrorString() );
|
||||||
|
}
|
||||||
else if ( expr.hasEvalError() )
|
else if ( expr.hasEvalError() )
|
||||||
errStr = expr.evalErrorString();
|
{
|
||||||
|
errors << tr( "Evaluation error: %1" ).arg( expr.evalErrorString() );
|
||||||
|
}
|
||||||
else if ( ! mValidConstraint )
|
else if ( ! mValidConstraint )
|
||||||
errStr = tr( "predicate is False" );
|
{
|
||||||
|
errors << tr( "%1 check failed" ).arg( layer()->editFormConfig().constraintDescription( mFieldIdx ) );
|
||||||
|
}
|
||||||
|
|
||||||
toEmit = true;
|
toEmit = true;
|
||||||
}
|
}
|
||||||
@ -138,30 +150,66 @@ void QgsEditorWidgetWrapper::updateConstraint( const QgsFeature &ft )
|
|||||||
|
|
||||||
if ( layer()->fieldConstraints( mFieldIdx ) & QgsField::ConstraintNotNull )
|
if ( layer()->fieldConstraints( mFieldIdx ) & QgsField::ConstraintNotNull )
|
||||||
{
|
{
|
||||||
|
descriptions << QStringLiteral( "NotNull" );
|
||||||
if ( !expression.isEmpty() )
|
if ( !expression.isEmpty() )
|
||||||
{
|
{
|
||||||
QString fieldName = ft.fields().field( mFieldIdx ).name();
|
expressions << fieldName + " IS NOT NULL";
|
||||||
expression = "( " + expression + " ) AND ( " + fieldName + " IS NOT NULL)";
|
|
||||||
description = "( " + description + " ) AND NotNull";
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
description = QStringLiteral( "NotNull" );
|
expressions << QStringLiteral( "NotNull" );
|
||||||
expression = QStringLiteral( "NotNull" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mValidConstraint = mValidConstraint && !value.isNull();
|
mValidConstraint = mValidConstraint && !value.isNull();
|
||||||
|
|
||||||
if ( 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;
|
toEmit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( toEmit )
|
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 );
|
updateConstraintWidgetStatus( mValidConstraint );
|
||||||
emit constraintStatusChanged( expression, description, errStr, mValidConstraint );
|
emit constraintStatusChanged( expressionDesc, description, errStr, mValidConstraint );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,6 +218,11 @@ bool QgsEditorWidgetWrapper::isValidConstraint() const
|
|||||||
return mValidConstraint;
|
return mValidConstraint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString QgsEditorWidgetWrapper::constraintFailureReason() const
|
||||||
|
{
|
||||||
|
return mConstraintFailureReason;
|
||||||
|
}
|
||||||
|
|
||||||
bool QgsEditorWidgetWrapper::isInTable( const QWidget* parent )
|
bool QgsEditorWidgetWrapper::isInTable( const QWidget* parent )
|
||||||
{
|
{
|
||||||
if ( !parent ) return false;
|
if ( !parent ) return false;
|
||||||
|
@ -124,12 +124,21 @@ class GUI_EXPORT QgsEditorWidgetWrapper : public QgsWidgetWrapper
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current constraint status.
|
* 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
|
* false otherwise
|
||||||
* @note added in QGIS 2.16
|
* @note added in QGIS 2.16
|
||||||
|
* @see constraintFailureReason()
|
||||||
*/
|
*/
|
||||||
bool isValidConstraint() const;
|
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:
|
signals:
|
||||||
/**
|
/**
|
||||||
* Emit this signal, whenever the value changed.
|
* Emit this signal, whenever the value changed.
|
||||||
@ -239,6 +248,9 @@ class GUI_EXPORT QgsEditorWidgetWrapper : public QgsWidgetWrapper
|
|||||||
*/
|
*/
|
||||||
bool mValidConstraint;
|
bool mValidConstraint;
|
||||||
|
|
||||||
|
//! Contains the string explanation of why a constraint check failed
|
||||||
|
QString mConstraintFailureReason;
|
||||||
|
|
||||||
int mFieldIdx;
|
int mFieldIdx;
|
||||||
QgsFeature mFeature;
|
QgsFeature mFeature;
|
||||||
mutable QVariant mDefaultValue; // Cache default value, we don't want to retrieve different serial numbers if called repeatedly
|
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() );
|
invalidFields.append( eww->field().name() );
|
||||||
|
|
||||||
QString desc = eww->layer()->editFormConfig().constraintDescription( eww->fieldIdx() );
|
descriptions.append( eww->constraintFailureReason() );
|
||||||
descriptions.append( desc );
|
|
||||||
|
|
||||||
valid = false; // continue to get all invalif fields
|
valid = false; // continue to get all invalif fields
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user