Qt и NULL во внешних ключах
Время от времени мы сталкиваемся с ситуацией, когда поле таблицы, являющееся внешним ключом, содержит значения NULL. С точки зрения проектирования баз данных это ошибка, которую следует избегать. Но, предположим, избежать ее не удалось (базу данных проектировал другой человек, было это давно, внести изменения нельзя и т.д.). Проблема в том, что при работе с объектами QSqlRelationalTableModel строка, которая содержит NULL во внешнем ключе, вообще не будет отображаться. Это связано с тем, как QSqlRelationalTableModel генерирует запросы SQL.
Рассмотрим пример. Путь у нас есть две таблицы
Поле Items.Color — это внешний ключ, который ссылается на Colors.id. Предположим, что содержимое таблицы Items выглядит так:
То есть, сдержит NULL в поле внешнего ключа. При работе с QSqlRelationalTableModel строка 3 не будет отображаться. Было предложено несколько решений проблемы, разной степени неуклюжести. Я предлагаю решение, которое кажется мне наиболее гибким и универсальным. Суть его в том, чтобы возложить обработку отношений между таблицами на класс-делегат. Специальный класс WeakRelationalDelegate позволяет устанавливать отношения между таблицами, даже если они содержат значения NULL в полях внешних ключей. Объявление класса выгладит следующим образом:
class WeakRelationalDelegate : public QStyledItemDelegate
{
public:
WeakRelationalDelegate(QObject * parent = 0);
void addRelation(const QString &fkColumn, const QString &extTableName, const QString &extIDColumn, const QString &extDisplayColumn);
void refreshRelations();
protected:
QWidget * createEditor ( QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData ( QWidget * editor, const QModelIndex & index ) const;
void updateEditorGeometry ( QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
void setModelData ( QWidget * editor, QAbstractItemModel * model, const QModelIndex & index ) const;
private:
TableMap tables;
};
Вот как выглядит применение класса WeakRelationalDelegate в нашем примере:
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
db.setDatabaseName(QString("Driver={SQL SERVER};Server=LOCAL;Database=test;UID=sa;PWD=Password123"));
if (! db.open()) {
qDebug() << "not open";
return;
}
QSqlTableModel * m = new QSqlTableModel(this);
m->setTable("Items");
WeakRelationalDelegate * wd = new WeakRelationalDelegate(this);
wd->addRelation("Color","Colors", "Id", "Name");
ui->tableView->setItemDelegate(wd);
m->select();
ui->tableView->setModel(m);
ui->tableView->show();
Метод addRelation() создает отношение между таблицей Items, представленной моделью m, и таблицей Colors. Первый аргумент метода — имя поля таблицы Items, являющегося внешним ключом (между прочим, это поле не обязательно должно быть объявлено как внешний ключ в самой БД). Второй аргумент — имя таблицы, на поле которой ссылается внешний ключ. Третий аргумент — имя поля, на которое ссылается внешний ключ. Четвертый аргумент — имя поля таблицы Colors, значениями которого должно быть заменено поле Color в таблице Items. Обратите внимание, что в качестве модели таблицы Items мы можем использовать как объект QSqlRelationalTableModel, так и объект QSqlTableModel. В результате получим вот что:
Для редактирования поля внешнего ключа используется раскрывающийся список, как и в случае использования QSqlRelationalDelegate. Исходные тексты класса, приложения примера и базы данных вы найдете на этой странице.
© 2011 Андрей Боровский