Skip to content

Commit

Permalink
Add auto-discovery of relations for PostgresQL
Browse files Browse the repository at this point in the history
Fixed the add relation functionnality: the table is sorted. When the code
was setting the sorted column, the row was sorted and the other columns it was
setting were set on the wrong row.
  • Loading branch information
Patrick Valsecchi authored and pvalsecc committed Sep 27, 2016
1 parent 9cf9938 commit 31a1c23
Show file tree
Hide file tree
Showing 19 changed files with 495 additions and 8 deletions.
15 changes: 15 additions & 0 deletions python/core/qgsrelation.sip
Expand Up @@ -171,6 +171,12 @@ class QgsRelation
*/
QString id() const;

/**
* Generate a (project-wide) unique id for this relation
* @note added in QGIS 3.0
*/
void generateId();

/**
* Access the referencing (child) layer's id
* This is the layer which has the field(s) which point to another layer
Expand Down Expand Up @@ -241,6 +247,15 @@ class QgsRelation
*/
bool isValid() const;

/**
* Compares the two QgsRelation, ignoring the name and the ID.
*
* @param other The other relation
* @return true if they are similar
* @note added in QGIS 3.0
*/
bool hasEqualDefinition( const QgsRelation& other ) const;

protected:
/**
* Updates the validity status of this relation.
Expand Down
10 changes: 10 additions & 0 deletions python/core/qgsrelationmanager.sip
Expand Up @@ -90,6 +90,16 @@ class QgsRelationManager : QObject
*/
QList<QgsRelation> referencedRelations( QgsVectorLayer *layer = 0 ) const;

/**
* Discover all the relations available from the current layers.
*
* @param existingRelations the existing relations to filter them out
* @param layers the current layers
* @return the list of discovered relations
* @note added in QGIS 3.0
*/
static QList<QgsRelation> discoverRelations( const QList<QgsRelation>& existingRelations, const QList<QgsVectorLayer*>& layers );

signals:
/** This signal is emitted when the relations were loaded after reading a project */
void relationsLoaded();
Expand Down
9 changes: 9 additions & 0 deletions python/core/qgsvectordataprovider.sip
Expand Up @@ -371,6 +371,15 @@ class QgsVectorDataProvider : QgsDataProvider
*/
virtual QSet<QgsMapLayerDependency> dependencies() const;

/**
* Discover the available relations with the given layers.
* @param self the layer using this data provider.
* @param layers the other layers.
* @return the list of N-1 relations from this provider.
* @note added in QGIS 3.0
*/
virtual QList<QgsRelation> discoverRelations( const QgsVectorLayer* self, const QList<QgsVectorLayer*>& layers ) const;

signals:
/** Signals an error in this provider */
void raiseError( const QString& msg );
Expand Down
2 changes: 2 additions & 0 deletions src/app/CMakeLists.txt
Expand Up @@ -28,6 +28,7 @@ SET(QGIS_APP_SRCS
qgsdecorationscalebardialog.cpp
qgsdecorationgrid.cpp
qgsdecorationgriddialog.cpp
qgsdiscoverrelationsdlg.cpp
qgsdxfexportdialog.cpp
qgsformannotationdialog.cpp
qgsguivectorlayertools.cpp
Expand Down Expand Up @@ -207,6 +208,7 @@ SET (QGIS_APP_MOC_HDRS
qgsdecorationgriddialog.h
qgsdelattrdialog.h
qgsdiagramproperties.h
qgsdiscoverrelationsdlg.h
qgsdisplayangle.h
qgsdxfexportdialog.h
qgsfeatureaction.h
Expand Down
61 changes: 61 additions & 0 deletions src/app/qgsdiscoverrelationsdlg.cpp
@@ -0,0 +1,61 @@
/***************************************************************************
qgsdiscoverrelationsdlg.cpp
---------------------
begin : September 2016
copyright : (C) 2016 by Patrick Valsecchi
email : patrick dot valsecchi at camptocamp 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 "qgsdiscoverrelationsdlg.h"
#include "qgsvectorlayer.h"
#include "qgsrelationmanager.h"

#include <QPushButton>

QgsDiscoverRelationsDlg::QgsDiscoverRelationsDlg( const QList<QgsRelation>& existingRelations, const QList<QgsVectorLayer*>& layers, QWidget *parent )
: QDialog( parent )
, mLayers( layers )
{
setupUi( this );

mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
connect( mRelationsTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsDiscoverRelationsDlg::onSelectionChanged );

mFoundRelations = QgsRelationManager::discoverRelations( existingRelations, layers );
Q_FOREACH ( const QgsRelation& relation, mFoundRelations ) addRelation( relation );

mRelationsTable->resizeColumnsToContents();

}

void QgsDiscoverRelationsDlg::addRelation( const QgsRelation &rel )
{
const int row = mRelationsTable->rowCount();
mRelationsTable->insertRow( row );
mRelationsTable->setItem( row, 0, new QTableWidgetItem( rel.name() ) );
mRelationsTable->setItem( row, 1, new QTableWidgetItem( rel.referencingLayer()->name() ) );
mRelationsTable->setItem( row, 2, new QTableWidgetItem( rel.fieldPairs().at( 0 ).referencingField() ) );
mRelationsTable->setItem( row, 3, new QTableWidgetItem( rel.referencedLayer()->name() ) );
mRelationsTable->setItem( row, 4, new QTableWidgetItem( rel.fieldPairs().at( 0 ).referencedField() ) );
}

QList<QgsRelation> QgsDiscoverRelationsDlg::relations() const
{
QList<QgsRelation> result;
Q_FOREACH ( const QModelIndex& row, mRelationsTable->selectionModel()->selectedRows() )
{
result.append( mFoundRelations.at( row.row() ) );
}
return result;
}

void QgsDiscoverRelationsDlg::onSelectionChanged()
{
mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( mRelationsTable->selectionModel()->hasSelection() );
}
53 changes: 53 additions & 0 deletions src/app/qgsdiscoverrelationsdlg.h
@@ -0,0 +1,53 @@
/***************************************************************************
qgsdiscoverrelationsdlg.h
---------------------
begin : September 2016
copyright : (C) 2016 by Patrick Valsecchi
email : patrick dot valsecchi at camptocamp 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 QGSDISCOVERRELATIONSDLG_H
#define QGSDISCOVERRELATIONSDLG_H

#include <QDialog>
#include "ui_qgsdiscoverrelationsdlgbase.h"
#include "qgsrelation.h"

class QgsRelationManager;
class QgsVectorLayer;

/**
* Shows the list of relations discovered from the providers.
*
* The user can select some of them to add them to his project.
*/
class APP_EXPORT QgsDiscoverRelationsDlg : public QDialog, private Ui::QgsDiscoverRelationsDlgBase
{
Q_OBJECT

public:
explicit QgsDiscoverRelationsDlg( const QList<QgsRelation>& existingRelations, const QList<QgsVectorLayer*>& layers, QWidget *parent = nullptr );

/**
* Get the selected relations.
*/
QList<QgsRelation> relations() const;

private slots:
void onSelectionChanged();

private:
QList<QgsVectorLayer*> mLayers;
QList<QgsRelation> mFoundRelations;

void addRelation( const QgsRelation &rel );

};

#endif // QGSDISCOVERRELATIONSDLG_H
16 changes: 15 additions & 1 deletion src/app/qgsrelationmanagerdialog.cpp
Expand Up @@ -13,6 +13,7 @@
* *
***************************************************************************/

#include "qgsdiscoverrelationsdlg.h"
#include "qgsrelationadddlg.h"
#include "qgsrelationmanagerdialog.h"
#include "qgsrelationmanager.h"
Expand Down Expand Up @@ -46,6 +47,7 @@ void QgsRelationManagerDialog::setLayers( const QList< QgsVectorLayer* >& layers

void QgsRelationManagerDialog::addRelation( const QgsRelation &rel )
{
mRelationsTable->setSortingEnabled( false );
int row = mRelationsTable->rowCount();
mRelationsTable->insertRow( row );

Expand All @@ -54,7 +56,6 @@ void QgsRelationManagerDialog::addRelation( const QgsRelation &rel )
item->setData( Qt::UserRole, QVariant::fromValue<QgsRelation>( rel ) );
mRelationsTable->setItem( row, 0, item );


item = new QTableWidgetItem( rel.referencingLayer()->name() );
item->setFlags( Qt::ItemIsEditable );
mRelationsTable->setItem( row, 1, item );
Expand All @@ -74,6 +75,7 @@ void QgsRelationManagerDialog::addRelation( const QgsRelation &rel )
item = new QTableWidgetItem( rel.id() );
item->setFlags( Qt::ItemIsEditable );
mRelationsTable->setItem( row, 5, item );
mRelationsTable->setSortingEnabled( true );
}

void QgsRelationManagerDialog::on_mBtnAddRelation_clicked()
Expand Down Expand Up @@ -118,6 +120,18 @@ void QgsRelationManagerDialog::on_mBtnAddRelation_clicked()
}
}

void QgsRelationManagerDialog::on_mBtnDiscoverRelations_clicked()
{
QgsDiscoverRelationsDlg discoverDlg( relations(), mLayers, this );
if ( discoverDlg.exec() )
{
Q_FOREACH ( const QgsRelation& relation, discoverDlg.relations() )
{
addRelation( relation );
}
}
}

void QgsRelationManagerDialog::on_mBtnRemoveRelation_clicked()
{
if ( mRelationsTable->currentIndex().isValid() )
Expand Down
1 change: 1 addition & 0 deletions src/app/qgsrelationmanagerdialog.h
Expand Up @@ -39,6 +39,7 @@ class APP_EXPORT QgsRelationManagerDialog : public QWidget, private Ui::QgsRelat

public slots:
void on_mBtnAddRelation_clicked();
void on_mBtnDiscoverRelations_clicked();
void on_mBtnRemoveRelation_clicked();

private:
Expand Down
15 changes: 15 additions & 0 deletions src/core/qgsrelation.cpp
Expand Up @@ -248,6 +248,16 @@ QString QgsRelation::id() const
return mRelationId;
}

void QgsRelation::generateId()
{
mRelationId = QString( "%1_%2_%3_%4" )
.arg( referencingLayerId(),
mFieldPairs.at( 0 ).referencingField(),
referencedLayerId(),
mFieldPairs.at( 0 ).referencedField() );
updateRelationStatus();
}

QString QgsRelation::referencingLayerId() const
{
return mReferencingLayerId;
Expand Down Expand Up @@ -301,6 +311,11 @@ bool QgsRelation::isValid() const
return mValid;
}

bool QgsRelation::hasEqualDefinition( const QgsRelation& other ) const
{
return mReferencedLayerId == other.mReferencedLayerId && mReferencingLayerId == other.mReferencingLayerId && mFieldPairs == other.mFieldPairs;
}

void QgsRelation::updateRelationStatus()
{
const QMap<QString, QgsMapLayer*>& mapLayers = QgsMapLayerRegistry::instance()->mapLayers();
Expand Down
17 changes: 17 additions & 0 deletions src/core/qgsrelation.h
Expand Up @@ -57,6 +57,8 @@ class CORE_EXPORT QgsRelation
QString referencingField() const { return first; }
//! Get the name of the referenced (parent) field
QString referencedField() const { return second; }

bool operator==( const FieldPair& other ) const { return first == other.first && second == other.second; }
};

/**
Expand Down Expand Up @@ -210,6 +212,12 @@ class CORE_EXPORT QgsRelation
*/
QString id() const;

/**
* Generate a (project-wide) unique id for this relation
* @note added in QGIS 3.0
*/
void generateId();

/**
* Access the referencing (child) layer's id
* This is the layer which has the field(s) which point to another layer
Expand Down Expand Up @@ -272,6 +280,15 @@ class CORE_EXPORT QgsRelation
*/
bool isValid() const;

/**
* Compares the two QgsRelation, ignoring the name and the ID.
*
* @param other The other relation
* @return true if they are similar
* @note added in QGIS 3.0
*/
bool hasEqualDefinition( const QgsRelation& other ) const;

protected:
/**
* Updates the validity status of this relation.
Expand Down
26 changes: 26 additions & 0 deletions src/core/qgsrelationmanager.cpp
Expand Up @@ -19,6 +19,7 @@
#include "qgslogger.h"
#include "qgsmaplayerregistry.h"
#include "qgsproject.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"

QgsRelationManager::QgsRelationManager( QgsProject* project )
Expand Down Expand Up @@ -217,3 +218,28 @@ void QgsRelationManager::layersRemoved( const QStringList& layers )
emit changed();
}
}

static bool hasRelationWithEqualDefinition( const QList<QgsRelation>& existingRelations, const QgsRelation& relation )
{
Q_FOREACH ( const QgsRelation& cur, existingRelations )
{
if ( cur.hasEqualDefinition( relation ) ) return true;
}
return false;
}

QList<QgsRelation> QgsRelationManager::discoverRelations( const QList<QgsRelation>& existingRelations, const QList<QgsVectorLayer*>& layers )
{
QList<QgsRelation> result;
Q_FOREACH ( const QgsVectorLayer* layer, layers )
{
Q_FOREACH ( const QgsRelation& relation, layer->dataProvider()->discoverRelations( layer, layers ) )
{
if ( !hasRelationWithEqualDefinition( existingRelations, relation ) )
{
result.append( relation );
}
}
}
return result;
}
10 changes: 10 additions & 0 deletions src/core/qgsrelationmanager.h
Expand Up @@ -117,6 +117,16 @@ class CORE_EXPORT QgsRelationManager : public QObject
*/
QList<QgsRelation> referencedRelations( QgsVectorLayer *layer = nullptr ) const;

/**
* Discover all the relations available from the current layers.
*
* @param existingRelations the existing relations to filter them out
* @param layers the current layers
* @return the list of discovered relations
* @note added in QGIS 3.0
*/
static QList<QgsRelation> discoverRelations( const QList<QgsRelation>& existingRelations, const QList<QgsVectorLayer*>& layers );

signals:
/** This signal is emitted when the relations were loaded after reading a project */
void relationsLoaded();
Expand Down
5 changes: 5 additions & 0 deletions src/core/qgsvectordataprovider.cpp
Expand Up @@ -718,3 +718,8 @@ QgsGeometry* QgsVectorDataProvider::convertToProviderType( const QgsGeometry& ge
}

QStringList QgsVectorDataProvider::smEncodings;

QList<QgsRelation> QgsVectorDataProvider::discoverRelations( const QgsVectorLayer*, const QList<QgsVectorLayer*>& ) const
{
return QList<QgsRelation>();
}

0 comments on commit 31a1c23

Please sign in to comment.