PblSqlRelationalTabelModel

фотка 1

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

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

Что делать?

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

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

Но тем нк менее сама идея изменять исходники, а точнее развивать их вполне правиная.

Сделаем своего наследника класса QSqlTableModel, но в составе исходников Qt.

Унаследуем также приватный класс QSqlTableModelPrivate, все в стиле самого QSqlTableModel, который наследуется от QSqlQueryModel.

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

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

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

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

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

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

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

Основная идея в том, что надо переопределить метод selectStatement класса QSqlTableModel.

фотка 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.