2016-11-01 3 views
0

Nehmen wir an, ich habe eine Tabelle 'Person' mit den folgenden Spalten: ID, Name, Manager-ID. Dabei ist "id" der Primärschlüssel und "manager_id" der Fremdschlüssel. Da einige Leute jetzt einen Manager haben, darf dieser Wert NULL sein. Dies scheint jedoch Probleme mit dem QSqlRelationalTableMod von Qt zu verursachen.Qt: QSqlRelationalTableModel Verweis auf nicht vorhandene Fremdschlüssel

Hier ist ein minimalistisches Beispiel, das das Problem repliziert: window.cpp:

Window::Window(QWidget *parent) : QWidget(parent) 
{ 
// setup database 
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); 
db.setDatabaseName(":memory:"); 
db.open(); 

// create database 
QSqlQuery query; 
query.exec("create table 'person' (id INTEGER NOT NULL PRIMARY KEY, " 
      "name varchar(20), manager_id INTEGER NULL)"); 
query.exec("insert into person values(1, 'Alice', 2)"); 
query.exec("insert into person values(2, 'Bob', -1)"); // invalid manager_id 
//query.exec("insert into person values(2, 'Bob', 1)"); // valid example 

// setup model 
model = new QSqlRelationalTableModel(this); 
model->setTable("person"); 
model->setEditStrategy(QSqlTableModel::OnRowChange); 

// setup foreign key 
int typeIndex = model->fieldIndex("manager_id"); 
model->setRelation(typeIndex, QSqlRelation("person", "id", "name")); 
model->select(); 

// setup UI 
auto nameLabel = new QLabel(tr("Name:")); auto nameEdit = new QLineEdit(); 
auto typeLabel = new QLabel(tr("Manager:")); auto typeComboBox = new QComboBox(); 
auto nextButton = new QPushButton(tr("Next")); 
auto previousButton = new QPushButton(tr("Previous")); 
QSqlTableModel *relModel = model->relationModel(typeIndex); 
typeComboBox->setModel(relModel); 
typeComboBox->setModelColumn(relModel->fieldIndex("name")); 
QGridLayout *layout = new QGridLayout(); 
layout->addWidget(nameLabel, 0, 0, 1, 1); 
layout->addWidget(nameEdit, 0, 1, 1, 1); 
layout->addWidget(previousButton, 0, 2, 1, 1); 
layout->addWidget(nextButton, 1, 2, 1, 1); 
layout->addWidget(typeLabel, 2, 0, 1, 1); 
layout->addWidget(typeComboBox, 2, 1, 1, 1); 
setLayout(layout); 

// setup mapper 
mapper = new QDataWidgetMapper(this); 
mapper->setModel(model); 
mapper->setItemDelegate(new QSqlRelationalDelegate(this)); 
mapper->addMapping(nameEdit, model->fieldIndex("name")); 
mapper->addMapping(typeComboBox, typeIndex); 
mapper->toFirst(); 

connect(previousButton, SIGNAL(clicked()), mapper, SLOT(toPrevious())); 
connect(nextButton, SIGNAL(clicked()), mapper, SLOT(toNext())); 
} 

window.h:

#include <QWidget> 
class QDataWidgetMapper; 
class QSqlRelationalTableModel; 
class Window : public QWidget 
{ 
    Q_OBJECT 
public: 
    Window(QWidget *parent = 0); 
private slots: 
private: 
    QDataWidgetMapper *mapper; 
    QSqlRelationalTableModel *model; 
}; 

Das Problem ist, dass der zweite Datensatz (dh "Bob") wird nicht angezeigt, da die ID seines Managers ungültig ist (-1).

Die Dokumentation der QSqlRelationalTableModel -Statements: "Wenn eine relationale Tabelle Schlüssel enthält, die auf nicht vorhandene Zeilen in der referenzierten Tabelle verweisen, werden die Zeilen mit den ungültigen Schlüsseln nicht über das Modell verfügbar gemacht ist verantwortlich für die referentielle Integrität. "

Aber gibt es keinen Weg um diese? Es scheint mir, dass dies ein häufiges Problem ist. Vielen Dank.

+0

Eine "dreckige" Lösung, die ich finden könnte, ist das Hinzufügen einer 'Dummy'-Zeile in die Tabelle und dann das Ändern der Werte entsprechend: query.exec ("in person values ​​(1,' ', -1) einfügen ") ;. Aber das scheint ziemlich eklig zu sein. – user1829358

Antwort

1

Gemäß Ihrer Beschreibung der Daten ist "kein Manager" ein zulässiger Wert, daher ist es nicht sinnvoll, ihn als ungültig zu bezeichnen.

Das eigentliche Problem in Ihrem Beispiel ist, dass die Spalte name mehrere überlappende Positionen hat. Es sollte wirklich eine separate names Tabelle sein, die dann eine Zeile mit einem leeren String haben kann, um "no manager" anzuzeigen.

Die Tabelle enthält dann nur die IDs aus der Tabelle names.

+0

Ist das nicht viel wie der Kommentar, den ich gepostet habe? Es scheint mir eine schmutzige Lösung zu sein. Darüber hinaus sehe ich kein Problem mit Verweisen auf die gleiche Tabelle. Ist das nicht eine häufige Situation im Datenbankdesign? – user1829358

+0

@ user1829358. Für mich scheint es eine natürliche und effiziente Lösung zu sein, alle Namen in eine separate Tabelle zu stellen. Aber wenn ich gezwungen wäre, alles in einen Tisch zu legen, würde ich wohl keine Convenience-Klasse wie QSqlRelationalTableModel verwenden, wenn die auferlegten Einschränkungen nicht meinen speziellen Bedürfnissen entsprachen. In der Tat scheint es eine ziemlich schwergewichtige Lösung zu sein, wenn Sie nicht wirklich auf andere Datenbanktabellen verweisen. – ekhumoro

Verwandte Themen