mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-06 00:07:29 -04:00
[api] Add opt-in setting to make QgsVectorFileWriter transfer
field constraints to the output file This is opt in to avoid potentially breaking existing scripts/plugins.
This commit is contained in:
parent
8b3c8a789b
commit
dce7bbdeb1
@ -394,6 +394,8 @@ Constructor
|
||||
bool saveMetadata;
|
||||
|
||||
QgsLayerMetadata layerMetadata;
|
||||
|
||||
bool includeConstraints;
|
||||
};
|
||||
|
||||
|
||||
@ -750,6 +752,7 @@ Returns whether there are among the attributes specified some that do not exist
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
QgsVectorFileWriter( const QgsVectorFileWriter &rh );
|
||||
};
|
||||
|
@ -69,22 +69,20 @@ QgsVectorFileWriter::FieldValueConverter *QgsVectorFileWriter::FieldValueConvert
|
||||
return new FieldValueConverter( *this );
|
||||
}
|
||||
|
||||
QgsVectorFileWriter::QgsVectorFileWriter(
|
||||
const QString &vectorFileName,
|
||||
const QString &fileEncoding,
|
||||
const QgsFields &fields,
|
||||
Qgis::WkbType geometryType,
|
||||
const QgsCoordinateReferenceSystem &srs,
|
||||
const QString &driverName,
|
||||
const QStringList &datasourceOptions,
|
||||
const QStringList &layerOptions,
|
||||
QString *newFilename,
|
||||
Qgis::FeatureSymbologyExport symbologyExport,
|
||||
QgsFeatureSink::SinkFlags sinkFlags,
|
||||
QString *newLayer,
|
||||
const QgsCoordinateTransformContext &transformContext,
|
||||
FieldNameSource fieldNameSource
|
||||
)
|
||||
QgsVectorFileWriter::QgsVectorFileWriter( const QString &vectorFileName,
|
||||
const QString &fileEncoding,
|
||||
const QgsFields &fields,
|
||||
Qgis::WkbType geometryType,
|
||||
const QgsCoordinateReferenceSystem &srs,
|
||||
const QString &driverName,
|
||||
const QStringList &datasourceOptions,
|
||||
const QStringList &layerOptions,
|
||||
QString *newFilename,
|
||||
Qgis::FeatureSymbologyExport symbologyExport,
|
||||
QgsFeatureSink::SinkFlags sinkFlags,
|
||||
QString *newLayer,
|
||||
const QgsCoordinateTransformContext &transformContext,
|
||||
FieldNameSource fieldNameSource )
|
||||
: mError( NoError )
|
||||
, mWkbType( geometryType )
|
||||
, mSymbologyExport( symbologyExport )
|
||||
@ -95,29 +93,29 @@ QgsVectorFileWriter::QgsVectorFileWriter(
|
||||
QString(), CreateOrOverwriteFile, newLayer, sinkFlags, transformContext, fieldNameSource );
|
||||
}
|
||||
|
||||
QgsVectorFileWriter::QgsVectorFileWriter(
|
||||
const QString &vectorFileName,
|
||||
const QString &fileEncoding,
|
||||
const QgsFields &fields,
|
||||
Qgis::WkbType geometryType,
|
||||
const QgsCoordinateReferenceSystem &srs,
|
||||
const QString &driverName,
|
||||
const QStringList &datasourceOptions,
|
||||
const QStringList &layerOptions,
|
||||
QString *newFilename,
|
||||
Qgis::FeatureSymbologyExport symbologyExport,
|
||||
FieldValueConverter *fieldValueConverter,
|
||||
const QString &layerName,
|
||||
ActionOnExistingFile action,
|
||||
QString *newLayer,
|
||||
const QgsCoordinateTransformContext &transformContext,
|
||||
QgsFeatureSink::SinkFlags sinkFlags,
|
||||
FieldNameSource fieldNameSource
|
||||
)
|
||||
QgsVectorFileWriter::QgsVectorFileWriter( const QString &vectorFileName,
|
||||
const QString &fileEncoding,
|
||||
const QgsFields &fields,
|
||||
Qgis::WkbType geometryType,
|
||||
const QgsCoordinateReferenceSystem &srs,
|
||||
const QString &driverName,
|
||||
const QStringList &datasourceOptions,
|
||||
const QStringList &layerOptions,
|
||||
QString *newFilename,
|
||||
Qgis::FeatureSymbologyExport symbologyExport,
|
||||
FieldValueConverter *fieldValueConverter,
|
||||
const QString &layerName,
|
||||
ActionOnExistingFile action,
|
||||
QString *newLayer,
|
||||
const QgsCoordinateTransformContext &transformContext,
|
||||
QgsFeatureSink::SinkFlags sinkFlags,
|
||||
FieldNameSource fieldNameSource,
|
||||
bool includeConstraints )
|
||||
: mError( NoError )
|
||||
, mWkbType( geometryType )
|
||||
, mSymbologyExport( symbologyExport )
|
||||
, mSymbologyScale( 1.0 )
|
||||
, mIncludeConstraints( includeConstraints )
|
||||
{
|
||||
init( vectorFileName, fileEncoding, fields, geometryType, srs, driverName,
|
||||
datasourceOptions, layerOptions, newFilename, fieldValueConverter,
|
||||
@ -140,7 +138,7 @@ QgsVectorFileWriter *QgsVectorFileWriter::create(
|
||||
return new QgsVectorFileWriter( fileName, options.fileEncoding, fields, geometryType, srs,
|
||||
options.driverName, options.datasourceOptions, options.layerOptions,
|
||||
newFilename, options.symbologyExport, options.fieldValueConverter, options.layerName,
|
||||
options.actionOnExistingFile, newLayer, transformContext, sinkFlags, options.fieldNameSource );
|
||||
options.actionOnExistingFile, newLayer, transformContext, sinkFlags, options.fieldNameSource, options.includeConstraints );
|
||||
Q_NOWARN_DEPRECATED_POP
|
||||
}
|
||||
|
||||
@ -865,6 +863,18 @@ void QgsVectorFileWriter::init( QString vectorFileName,
|
||||
OGR_Fld_SetComment( fld.get(), mCodec->fromUnicode( attrField.comment() ).constData() );
|
||||
#endif
|
||||
|
||||
if ( mIncludeConstraints )
|
||||
{
|
||||
if ( attrField.constraints().constraints() & QgsFieldConstraints::ConstraintNotNull )
|
||||
{
|
||||
OGR_Fld_SetNullable( fld.get(), false );
|
||||
}
|
||||
if ( attrField.constraints().constraints() & QgsFieldConstraints::ConstraintUnique )
|
||||
{
|
||||
OGR_Fld_SetUnique( fld.get(), true );
|
||||
}
|
||||
}
|
||||
|
||||
// create the field
|
||||
QgsDebugMsgLevel( "creating field " + attrField.name() +
|
||||
" type " + QString( QVariant::typeToName( attrField.type() ) ) +
|
||||
|
@ -546,6 +546,15 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink
|
||||
* \since QGIS 3.20
|
||||
*/
|
||||
QgsLayerMetadata layerMetadata;
|
||||
|
||||
/**
|
||||
* Set to TRUE to transfer field constraints to the exported vector file.
|
||||
*
|
||||
* Support for field constraints depends on the output file format.
|
||||
*
|
||||
* \since QGIS 3.34
|
||||
*/
|
||||
bool includeConstraints = false;
|
||||
};
|
||||
|
||||
#ifndef SIP_RUN
|
||||
@ -626,6 +635,7 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink
|
||||
* \param transformContext transform context, needed if the output file srs is forced to specific crs (added in QGIS 3.10.3)
|
||||
* \param sinkFlags feature sink flags (added in QGIS 3.10.3)
|
||||
* \param fieldNameSource source for field names (since QGIS 3.18)
|
||||
* \param includeConstraints set to TRUE to copy field constraints to the destination layer (since QGIS 3.34)
|
||||
* \note not available in Python bindings
|
||||
* \deprecated Use create() instead.
|
||||
*/
|
||||
@ -645,7 +655,8 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink
|
||||
QString *newLayer = nullptr,
|
||||
const QgsCoordinateTransformContext &transformContext = QgsCoordinateTransformContext(),
|
||||
QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags(),
|
||||
FieldNameSource fieldNameSource = Original
|
||||
FieldNameSource fieldNameSource = Original,
|
||||
bool includeConstraints = false
|
||||
) SIP_SKIP;
|
||||
|
||||
//! QgsVectorFileWriter cannot be copied.
|
||||
@ -978,6 +989,9 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink
|
||||
//! Field value converter
|
||||
FieldValueConverter *mFieldValueConverter = nullptr;
|
||||
|
||||
//! Whether to transfer field constraints to output
|
||||
bool mIncludeConstraints = false;
|
||||
|
||||
private:
|
||||
#ifdef SIP_RUN
|
||||
QgsVectorFileWriter( const QgsVectorFileWriter &rh );
|
||||
|
@ -50,6 +50,7 @@ from qgis.core import (
|
||||
QgsVectorFileWriter,
|
||||
QgsVectorLayer,
|
||||
QgsWkbTypes,
|
||||
QgsFieldConstraints
|
||||
)
|
||||
import unittest
|
||||
from qgis.testing import start_app, QgisTestCase
|
||||
@ -1715,6 +1716,95 @@ class TestQgsVectorFileWriter(QgisTestCase):
|
||||
self.assertFalse(
|
||||
writer.capabilities() & Qgis.VectorFileWriterCapability.FieldComments)
|
||||
|
||||
def testWriteFieldConstraints(self):
|
||||
"""
|
||||
Test explicitly including field constraints.
|
||||
"""
|
||||
layer = QgsVectorLayer(
|
||||
('Point?crs=epsg:4326&field=name:string(20)&'
|
||||
'field=age:integer&field=size:double'),
|
||||
'test',
|
||||
'memory')
|
||||
|
||||
self.assertTrue(layer.isValid())
|
||||
myProvider = layer.dataProvider()
|
||||
|
||||
layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintNotNull)
|
||||
layer.setFieldConstraint(2, QgsFieldConstraints.ConstraintUnique)
|
||||
|
||||
ft = QgsFeature()
|
||||
ft.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10, 10)))
|
||||
ft.setAttributes(['Johny', 20, 0.3])
|
||||
myResult, myFeatures = myProvider.addFeatures([ft])
|
||||
self.assertTrue(myResult)
|
||||
self.assertTrue(myFeatures)
|
||||
|
||||
options = QgsVectorFileWriter.SaveVectorOptions()
|
||||
options.includeConstraints = True
|
||||
|
||||
dest = os.path.join(str(QDir.tempPath()), 'constraints.gpkg')
|
||||
result, err = QgsVectorFileWriter.writeAsVectorFormatV2(
|
||||
layer,
|
||||
dest,
|
||||
QgsProject.instance().transformContext(),
|
||||
options)
|
||||
self.assertEqual(result, QgsVectorFileWriter.NoError)
|
||||
|
||||
res = QgsVectorLayer(dest, 'result')
|
||||
self.assertTrue(res.isValid())
|
||||
self.assertEqual([f.name() for f in res.fields()], ['fid', 'name', 'age', 'size'])
|
||||
|
||||
self.assertEqual(res.fields()['name'].constraints().constraints(),
|
||||
QgsFieldConstraints.Constraints())
|
||||
self.assertEqual(res.fields()['age'].constraints().constraints(),
|
||||
QgsFieldConstraints.ConstraintNotNull)
|
||||
self.assertEqual(res.fields()['size'].constraints().constraints(),
|
||||
QgsFieldConstraints.ConstraintUnique)
|
||||
|
||||
def testWriteSkipFieldConstraints(self):
|
||||
"""
|
||||
Test that default is to skip field constraints.
|
||||
"""
|
||||
layer = QgsVectorLayer(
|
||||
('Point?crs=epsg:4326&field=name:string(20)&'
|
||||
'field=age:integer&field=size:double'),
|
||||
'test',
|
||||
'memory')
|
||||
|
||||
self.assertTrue(layer.isValid())
|
||||
myProvider = layer.dataProvider()
|
||||
|
||||
layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintNotNull)
|
||||
layer.setFieldConstraint(2, QgsFieldConstraints.ConstraintUnique)
|
||||
|
||||
ft = QgsFeature()
|
||||
ft.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10, 10)))
|
||||
ft.setAttributes(['Johny', 20, 0.3])
|
||||
myResult, myFeatures = myProvider.addFeatures([ft])
|
||||
self.assertTrue(myResult)
|
||||
self.assertTrue(myFeatures)
|
||||
|
||||
options = QgsVectorFileWriter.SaveVectorOptions()
|
||||
|
||||
dest = os.path.join(str(QDir.tempPath()), 'constraints.gpkg')
|
||||
result, err = QgsVectorFileWriter.writeAsVectorFormatV2(
|
||||
layer,
|
||||
dest,
|
||||
QgsProject.instance().transformContext(),
|
||||
options)
|
||||
self.assertEqual(result, QgsVectorFileWriter.NoError)
|
||||
|
||||
res = QgsVectorLayer(dest, 'result')
|
||||
self.assertTrue(res.isValid())
|
||||
self.assertEqual([f.name() for f in res.fields()], ['fid', 'name', 'age', 'size'])
|
||||
|
||||
self.assertEqual(res.fields()['name'].constraints().constraints(),
|
||||
QgsFieldConstraints.Constraints())
|
||||
self.assertEqual(res.fields()['age'].constraints().constraints(),
|
||||
QgsFieldConstraints.Constraints())
|
||||
self.assertEqual(res.fields()['size'].constraints().constraints(),
|
||||
QgsFieldConstraints.Constraints())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
Loading…
x
Reference in New Issue
Block a user