mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-17 00:09:36 -04:00
Add a model for schemas from a database connection
This commit is contained in:
parent
2eb4eaff86
commit
a0a57d5bf7
69
python/core/auto_generated/qgsdatabaseschemamodel.sip.in
Normal file
69
python/core/auto_generated/qgsdatabaseschemamodel.sip.in
Normal file
@ -0,0 +1,69 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/qgsdatabaseschemamodel.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsDatabaseSchemaModel : QAbstractItemModel
|
||||
{
|
||||
%Docstring
|
||||
A model containing schemas from a database connection.
|
||||
|
||||
This class does not automatically subscribe to database updates. Schemas are queried
|
||||
from the database initially upon model construction. In order
|
||||
to update the listed schemas, QgsDatabaseSchemaModel.refresh() must be manually
|
||||
called.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsdatabaseschemamodel.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
explicit QgsDatabaseSchemaModel( const QString &provider, const QString &connection, QObject *parent /TransferThis/ = 0 );
|
||||
%Docstring
|
||||
Constructor for QgsDatabaseSchemaModel, for the specified ``provider`` and ``connection`` name.
|
||||
|
||||
.. warning::
|
||||
|
||||
The ``provider`` must support the connection API methods in its QgsProviderMetadata implementation
|
||||
in order for the model to work correctly.
|
||||
%End
|
||||
|
||||
explicit QgsDatabaseSchemaModel( QgsAbstractDatabaseProviderConnection *connection /Transfer/, QObject *parent /TransferThis/ = 0 );
|
||||
|
||||
virtual QModelIndex parent( const QModelIndex &child ) const;
|
||||
|
||||
virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const;
|
||||
|
||||
virtual int columnCount( const QModelIndex &parent = QModelIndex() ) const;
|
||||
|
||||
virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const;
|
||||
|
||||
virtual QModelIndex index( int row, int column, const QModelIndex &parent ) const;
|
||||
|
||||
|
||||
public slots:
|
||||
|
||||
void refresh();
|
||||
%Docstring
|
||||
Refreshes the schema list by querying the underlying connection.
|
||||
%End
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/qgsdatabaseschemamodel.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
@ -36,6 +36,7 @@
|
||||
%Include auto_generated/qgscoordinatetransformcontext.sip
|
||||
%Include auto_generated/qgscredentials.sip
|
||||
%Include auto_generated/qgsdartmeasurement.sip
|
||||
%Include auto_generated/qgsdatabaseschemamodel.sip
|
||||
%Include auto_generated/qgsdatadefinedsizelegend.sip
|
||||
%Include auto_generated/qgsdataitem.sip
|
||||
%Include auto_generated/qgsdataitemprovider.sip
|
||||
|
@ -227,6 +227,7 @@ SET(QGIS_CORE_SRCS
|
||||
qgscoordinateutils.cpp
|
||||
qgscredentials.cpp
|
||||
qgsdartmeasurement.cpp
|
||||
qgsdatabaseschemamodel.cpp
|
||||
qgsdatadefinedsizelegend.cpp
|
||||
qgsdataitem.cpp
|
||||
qgsdataitemprovider.cpp
|
||||
@ -727,6 +728,7 @@ SET(QGIS_CORE_HDRS
|
||||
qgscoordinateutils.h
|
||||
qgscredentials.h
|
||||
qgsdartmeasurement.h
|
||||
qgsdatabaseschemamodel.h
|
||||
qgsdatadefinedsizelegend.h
|
||||
qgsdataitem.h
|
||||
qgsdataitemprovider.h
|
||||
|
120
src/core/qgsdatabaseschemamodel.cpp
Normal file
120
src/core/qgsdatabaseschemamodel.cpp
Normal file
@ -0,0 +1,120 @@
|
||||
/***************************************************************************
|
||||
qgsdatabaseschemamodel.cpp
|
||||
--------------------------------------
|
||||
Date : March 2020
|
||||
Copyright : (C) 2020 Nyall Dawson
|
||||
Email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgsdatabaseschemamodel.h"
|
||||
#include "qgsproviderregistry.h"
|
||||
#include "qgsprovidermetadata.h"
|
||||
#include "qgsabstractdatabaseproviderconnection.h"
|
||||
|
||||
QgsDatabaseSchemaModel::QgsDatabaseSchemaModel( const QString &provider, const QString &connection, QObject *parent )
|
||||
: QAbstractItemModel( parent )
|
||||
{
|
||||
QgsProviderMetadata *metadata = QgsProviderRegistry::instance()->providerMetadata( provider );
|
||||
Q_ASSERT( metadata );
|
||||
|
||||
mConnection.reset( dynamic_cast<QgsAbstractDatabaseProviderConnection *>( metadata->createConnection( connection ) ) );
|
||||
Q_ASSERT( mConnection );
|
||||
init();
|
||||
}
|
||||
|
||||
QgsDatabaseSchemaModel::QgsDatabaseSchemaModel( QgsAbstractDatabaseProviderConnection *connection, QObject *parent )
|
||||
: QAbstractItemModel( parent )
|
||||
, mConnection( connection )
|
||||
{
|
||||
Q_ASSERT( mConnection );
|
||||
init();
|
||||
}
|
||||
|
||||
void QgsDatabaseSchemaModel::init()
|
||||
{
|
||||
Q_ASSERT( mConnection->capabilities() & QgsAbstractDatabaseProviderConnection::Capability::Schemas );
|
||||
mSchemas = mConnection->schemas();
|
||||
}
|
||||
|
||||
QModelIndex QgsDatabaseSchemaModel::parent( const QModelIndex &child ) const
|
||||
{
|
||||
Q_UNUSED( child )
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
|
||||
int QgsDatabaseSchemaModel::rowCount( const QModelIndex &parent ) const
|
||||
{
|
||||
if ( parent.isValid() )
|
||||
return 0;
|
||||
|
||||
return mSchemas.count();
|
||||
}
|
||||
|
||||
int QgsDatabaseSchemaModel::columnCount( const QModelIndex &parent ) const
|
||||
{
|
||||
Q_UNUSED( parent )
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
QVariant QgsDatabaseSchemaModel::data( const QModelIndex &index, int role ) const
|
||||
{
|
||||
if ( !index.isValid() )
|
||||
return QVariant();
|
||||
|
||||
const QString schemaName = mSchemas.value( index.row() );
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
case Qt::ToolTipRole:
|
||||
{
|
||||
return schemaName;
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QModelIndex QgsDatabaseSchemaModel::index( int row, int column, const QModelIndex &parent ) const
|
||||
{
|
||||
if ( hasIndex( row, column, parent ) )
|
||||
{
|
||||
return createIndex( row, column, row );
|
||||
}
|
||||
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
void QgsDatabaseSchemaModel::refresh()
|
||||
{
|
||||
const QStringList newSchemas = mConnection->schemas();
|
||||
const QStringList oldSchemas = mSchemas;
|
||||
|
||||
for ( const QString &oldSchema : oldSchemas )
|
||||
{
|
||||
if ( !newSchemas.contains( oldSchema ) )
|
||||
{
|
||||
int r = mSchemas.indexOf( oldSchema );
|
||||
beginRemoveRows( QModelIndex(), r, r );
|
||||
mSchemas.removeAt( r );
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
|
||||
for ( const QString &newSchema : newSchemas )
|
||||
{
|
||||
if ( !mSchemas.contains( newSchema ) )
|
||||
{
|
||||
beginInsertRows( QModelIndex(), mSchemas.count(), mSchemas.count() );
|
||||
mSchemas.append( newSchema );
|
||||
endInsertRows();
|
||||
}
|
||||
}
|
||||
}
|
78
src/core/qgsdatabaseschemamodel.h
Normal file
78
src/core/qgsdatabaseschemamodel.h
Normal file
@ -0,0 +1,78 @@
|
||||
/***************************************************************************
|
||||
qgsdatabaseschemamodel.h
|
||||
--------------------------------------
|
||||
Date : March 2020
|
||||
Copyright : (C) 2020 Nyall Dawson
|
||||
Email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSDATABASESCHEMAMODEL_H
|
||||
#define QGSDATABASESCHEMAMODEL_H
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QStringList>
|
||||
#include <memory>
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgis_sip.h"
|
||||
|
||||
class QgsProviderMetadata;
|
||||
class QgsAbstractDatabaseProviderConnection;
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* \class QgsDatabaseSchemaModel
|
||||
* \brief A model containing schemas from a database connection.
|
||||
*
|
||||
* This class does not automatically subscribe to database updates. Schemas are queried
|
||||
* from the database initially upon model construction. In order
|
||||
* to update the listed schemas, QgsDatabaseSchemaModel::refresh() must be manually
|
||||
* called.
|
||||
*
|
||||
* \since QGIS 3.14
|
||||
*/
|
||||
class CORE_EXPORT QgsDatabaseSchemaModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsDatabaseSchemaModel, for the specified \a provider and \a connection name.
|
||||
*
|
||||
* \warning The \a provider must support the connection API methods in its QgsProviderMetadata implementation
|
||||
* in order for the model to work correctly.
|
||||
*/
|
||||
explicit QgsDatabaseSchemaModel( const QString &provider, const QString &connection, QObject *parent SIP_TRANSFERTHIS = nullptr );
|
||||
|
||||
explicit QgsDatabaseSchemaModel( QgsAbstractDatabaseProviderConnection *connection SIP_TRANSFER, QObject *parent SIP_TRANSFERTHIS = nullptr );
|
||||
|
||||
// QAbstractItemModel interface
|
||||
QModelIndex parent( const QModelIndex &child ) const override;
|
||||
int rowCount( const QModelIndex &parent = QModelIndex() ) const override;
|
||||
int columnCount( const QModelIndex &parent = QModelIndex() ) const override;
|
||||
QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override;
|
||||
QModelIndex index( int row, int column, const QModelIndex &parent ) const override;
|
||||
|
||||
public slots:
|
||||
|
||||
/**
|
||||
* Refreshes the schema list by querying the underlying connection.
|
||||
*/
|
||||
void refresh();
|
||||
|
||||
private:
|
||||
void init();
|
||||
std::unique_ptr< QgsAbstractDatabaseProviderConnection > mConnection;
|
||||
QStringList mSchemas;
|
||||
};
|
||||
|
||||
#endif // QGSDATABASESCHEMAMODEL_H
|
@ -186,7 +186,6 @@ ADD_PYTHON_TEST(PyQgsProjectionSelectionWidgets test_qgsprojectionselectionwidge
|
||||
ADD_PYTHON_TEST(PyQgsProjectMetadata test_qgsprojectmetadata.py)
|
||||
ADD_PYTHON_TEST(PyQgsPropertyOverrideButton test_qgspropertyoverridebutton.py)
|
||||
ADD_PYTHON_TEST(PyQgsProviderConnectionModel test_qgsproviderconnectionmodel.py)
|
||||
ADD_PYTHON_TEST(PyQgsProviderConnectionPostgres test_qgsproviderconnection_postgres.py)
|
||||
ADD_PYTHON_TEST(PyQgsProviderConnectionGpkg test_qgsproviderconnection_ogr_gpkg.py)
|
||||
ADD_PYTHON_TEST(TestQgsRandomMarkerSymbolLayer test_qgsrandommarkersymbollayer.py)
|
||||
ADD_PYTHON_TEST(PyQgsRange test_qgsrange.py)
|
||||
@ -304,6 +303,8 @@ IF (ENABLE_PGTEST)
|
||||
ADD_PYTHON_TEST(PyQgsAuthManagerPasswordPostgresTest test_authmanager_password_postgres.py)
|
||||
ADD_PYTHON_TEST(PyQgsAuthManagerOgrPostgresTest test_authmanager_ogr_postgres.py)
|
||||
ADD_PYTHON_TEST(PyQgsDbManagerPostgis test_db_manager_postgis.py)
|
||||
ADD_PYTHON_TEST(PyQgsDatabaseSchemaModel test_qgsdatabaseschemamodel.py)
|
||||
ADD_PYTHON_TEST(PyQgsProviderConnectionPostgres test_qgsproviderconnection_postgres.py)
|
||||
ENDIF (ENABLE_PGTEST)
|
||||
|
||||
IF (ENABLE_MSSQLTEST)
|
||||
|
115
tests/src/python/test_qgsdatabaseschemamodel.py
Normal file
115
tests/src/python/test_qgsdatabaseschemamodel.py
Normal file
@ -0,0 +1,115 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Unit tests for QgsDatabaseSchemaModel
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
"""
|
||||
__author__ = 'Nyall Dawson'
|
||||
__date__ = '07/03/2020'
|
||||
__copyright__ = 'Copyright 2020, The QGIS Project'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import os
|
||||
from qgis.core import (
|
||||
QgsDatabaseSchemaModel,
|
||||
QgsProviderRegistry,
|
||||
)
|
||||
from qgis.PyQt.QtCore import (
|
||||
QCoreApplication,
|
||||
QModelIndex,
|
||||
Qt
|
||||
)
|
||||
from qgis.testing import unittest, start_app
|
||||
|
||||
|
||||
class TestPyQgsDatabaseSchemaModel(unittest.TestCase):
|
||||
|
||||
# Provider test cases must define the string URI for the test
|
||||
uri = ''
|
||||
# Provider test cases must define the provider name (e.g. "postgres" or "ogr")
|
||||
providerKey = 'postgres'
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Run before all tests"""
|
||||
|
||||
QCoreApplication.setOrganizationName("QGIS_Test")
|
||||
QCoreApplication.setOrganizationDomain(cls.__name__)
|
||||
QCoreApplication.setApplicationName(cls.__name__)
|
||||
start_app()
|
||||
cls.postgres_conn = "service='qgis_test'"
|
||||
if 'QGIS_PGTEST_DB' in os.environ:
|
||||
cls.postgres_conn = os.environ['QGIS_PGTEST_DB']
|
||||
cls.uri = cls.postgres_conn + ' sslmode=disable'
|
||||
|
||||
def testModel(self):
|
||||
conn = QgsProviderRegistry.instance().providerMetadata('postgres').createConnection(self.uri, {})
|
||||
self.assertTrue(conn)
|
||||
model = QgsDatabaseSchemaModel(conn)
|
||||
self.assertGreaterEqual(model.rowCount(), 3)
|
||||
old_count = model.rowCount()
|
||||
self.assertEqual(model.columnCount(), 1)
|
||||
schemas = [model.data(model.index(r, 0, QModelIndex()), Qt.DisplayRole) for r in range(model.rowCount())]
|
||||
self.assertIn('public', schemas)
|
||||
self.assertIn('CamelCaseSchema', schemas)
|
||||
self.assertIn('qgis_test', schemas)
|
||||
self.assertEqual(model.data(model.index(schemas.index('qgis_test'), 0, QModelIndex()), Qt.ToolTipRole), 'qgis_test')
|
||||
self.assertIsNone(model.data(model.index(model.rowCount(), 0, QModelIndex()), Qt.DisplayRole))
|
||||
|
||||
model.refresh()
|
||||
self.assertEqual(model.rowCount(), old_count)
|
||||
|
||||
conn.createSchema('myNewSchema')
|
||||
self.assertEqual(model.rowCount(), old_count)
|
||||
model.refresh()
|
||||
self.assertEqual(model.rowCount(), old_count + 1)
|
||||
schemas = [model.data(model.index(r, 0, QModelIndex()), Qt.DisplayRole) for r in range(model.rowCount())]
|
||||
self.assertIn('public', schemas)
|
||||
self.assertIn('CamelCaseSchema', schemas)
|
||||
self.assertIn('qgis_test', schemas)
|
||||
self.assertIn('myNewSchema', schemas)
|
||||
|
||||
conn.createSchema('myNewSchema2')
|
||||
conn.createSchema('myNewSchema3')
|
||||
model.refresh()
|
||||
self.assertEqual(model.rowCount(), old_count + 3)
|
||||
schemas = [model.data(model.index(r, 0, QModelIndex()), Qt.DisplayRole) for r in range(model.rowCount())]
|
||||
self.assertIn('public', schemas)
|
||||
self.assertIn('CamelCaseSchema', schemas)
|
||||
self.assertIn('qgis_test', schemas)
|
||||
self.assertIn('myNewSchema', schemas)
|
||||
self.assertIn('myNewSchema2', schemas)
|
||||
self.assertIn('myNewSchema3', schemas)
|
||||
|
||||
conn.createSchema('myNewSchema4')
|
||||
conn.dropSchema('myNewSchema2')
|
||||
conn.dropSchema('myNewSchema')
|
||||
model.refresh()
|
||||
self.assertEqual(model.rowCount(), old_count + 2)
|
||||
schemas = [model.data(model.index(r, 0, QModelIndex()), Qt.DisplayRole) for r in range(model.rowCount())]
|
||||
self.assertIn('public', schemas)
|
||||
self.assertIn('CamelCaseSchema', schemas)
|
||||
self.assertIn('qgis_test', schemas)
|
||||
self.assertNotIn('myNewSchema', schemas)
|
||||
self.assertNotIn('myNewSchema2', schemas)
|
||||
self.assertIn('myNewSchema3', schemas)
|
||||
self.assertIn('myNewSchema4', schemas)
|
||||
|
||||
conn.dropSchema('myNewSchema3')
|
||||
conn.dropSchema('myNewSchema4')
|
||||
model.refresh()
|
||||
self.assertEqual(model.rowCount(), old_count)
|
||||
schemas = [model.data(model.index(r, 0, QModelIndex()), Qt.DisplayRole) for r in range(model.rowCount())]
|
||||
self.assertIn('public', schemas)
|
||||
self.assertIn('CamelCaseSchema', schemas)
|
||||
self.assertIn('qgis_test', schemas)
|
||||
self.assertNotIn('myNewSchema3', schemas)
|
||||
self.assertNotIn('myNewSchema4', schemas)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
x
Reference in New Issue
Block a user