PblSqlRelationalTabelModel

фотка 1

Итак в Qt 4.8.1 функционал класса QSqlRelationalTableModel нас не устраивает, точнее нам нужен другой, на наш взгляд более удобный класс.

Почему QSqlRelationalTableModel не подходит см.setRelation.

Что делать?

А сделать можно оказывается следующее:

Примечание: на самом деле как оказалось впоследствие необязательно форкать исходники Qt, так как всего можно добиться стандартным наследованием открытых классов, смотрите Развитие Qt Sql

Подумав немного мы решили назвать свой класс PblSqlRelationalTabelModel, так как мы же не разработчики из компании Qt и префикс Q не для нас, у нас будет префикс Pbl.

Мы хотим, чтобы в нашем классе поле id внешней связи было всегда под рукой.

То есть setRelation также будет настраивать связь с внешней таблицей, но дополнительно, кроме текстовой замены, мы будем иметь поле id внешней связи в запросе. Наконец-то решим этот старый вопрос.

Заодно добавим функцию setCalcField для получение  sum, count и т.д. по данным полей из других таблиц.

И добавим вообще говоря все , что придет нам в голову.

А что так можно было? Давайте разберемся не бредим ли мы.

Пока проверять будем для SQLite, Qt 4.8.1, под Windows 10.

Основная идея в том, что надо переопределить метод selectStatement класса QSqlTableModel, который формирует запрос SELECT к базе данных. И формируется он только в одном месте - в selecetStatement методе.

фотка 2

Но сначала надо обратить внимание на setTable. Тут всегда сначала очищаются все данные от предыдущих запросов, все данные от других таблиц и т.д.

Сначала setTable делает запрос к базе данных, считывает данные по полям запрашиваемой таблицы и сохраняет их в параметре baseRec приватного класса.

Это оригинальные поля (будем так их называть). Мы же в нашем классе наследнике реализуем дополнительно расширенные поля, где и бедем собирать необходимую информацию по данным их других таблиц.

Внешние связи мы как обычно устанавливаем через стандартную  функцию setRelation для  полей, для которых есть внешние связи.

И реализуем свою функцию setCalcField для полей вычислений sum, count и т.д. по колонкам других таблиц, связанных с нашей таблицей.

Далее делаем select в модели данных, который вызывает переопределенный нами selectStatement (в нашем классе PblSqlRelationalTabelModel).

В итоге получается, что мы подменяем selectStatement, а точнее формированиие строки sql запроса на свой вариант, где добавляем в запросе получение дополнительных полей.

Конечно после выполнения измененного запроса состав полей может и скорее всего получится другой, то есть отличный от baseRec (добавятся еще поля).

Но самое приятное, что модель данных в Qt будет работать нормально в новых условиях.

QModelIndex-ы будут строится и для новых расширенных полей также как для оригинальных, так как это все есть "таблица", то есть select возвращает таблицу, где есть только колонки и строки.

Мы добавляем функцию setCalcFunc, чтобы вычислять итоговые суммы по колонкам других таблиц, у которых есть связь с нашей таблицей.

Мы создаем наш класс в стиле бинарной совместимости (как в Qt в основном все и реализовано). То есть наши setRelation и setCalcFunc будут хранить настройки где-то в приватном классе доступа к которому снаружи не будет (PblSqlRelationalTableModelPrivate).

Хотя надо отметить, что при сборке статически проекта беспокоится о бинарной совместимости не приходится. Можно почитать здесь: 
Настраиваем статику и динамику.

Самое важное, что наш класс PblSqlRelationalTabelModel не нарушает совместимость библиотек Qt, так как мы не изменяем исходники Qt ни в одном месте, а только добавляем новое развитие.

Небольшой визуальный пример, как это может выглядеть :

фотка 3

Это фрагмент программы, которую мы пишем для владельцев контрольно-кассовой техники БИТ драйвер ККТ.

Надо сказать, что ещё всё-таки надо развить класс QSqlRecord, чтобы из функции record(int row) мы получали не просто набор полей/значений строки таблицы из базы данных, но и информацию по relation полям, а именно в каких спец. полях находятся их значения id связей. fork QSqlRecord.

Фильтрация

Фильтрация работает штатно через функцию setFilter. Тут вы уже сами формируете строку фильтра и в общем-то для фильтра по расширенным полям, а именно по вычисляемые полям, все делается единообразно как и для оригинальных полей.

Сортировка

Сортировка или упорядочивание по полю работает штатно, то есть  сортируется все без проблем. Весь функционал сортировки  реализован в родительских классах.

Нам надо только переопределить сортировку по расширенным полям. Это делается в функции orderClause.