crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

3 страниц V   1 2 3 >  
Ответить в данную темуНачать новую тему
> Редактирование таблицы БД с уникальной комбинацией полей
Steklova Olga
  опции профиля:
сообщение 28.5.2013, 16:48
Сообщение #1


Участник
**

Группа: Участник
Сообщений: 198
Регистрация: 27.9.2011
Из: Санкт-Петербург
Пользователь №: 2912

Спасибо сказали: 5 раз(а)




Репутация:   4  


Всем привет :) Прошу не бить за многословие :rolleyes:

Я до сих пор редактировала таблицы с экрана только в том случае, когда надо было пометить какие-то записи. Для этого наследовала модель от QSqlTableModel, выставляла в flags для нужной колонки Qt::ItemIsUserCheckable, переопределяла setData и т.д.

А сейчас надо редактировать почти все поля таблиц. К тому же, значения некоторых полей должны составлять уникальную комбинацию, но быть при этом редактируемыми. Не подскажете, как проще сделать такое редактирование?
Подробнее...
У меня есть штук 15 вью, к каждому привязана своя модель с таблицей БД.
Вью расположены на страницах блокнота. На каждой странице - какая-то главная таблица и ее подчиненные.
Таблицы, например, такие:
CREATE TABLE T_OBJ (
    F_PK INTEGER NOT NULL, --первичный ключ, генератор

    F_V  SMALLINT NOT NULL, --|  
    F_A  SMALLINT NOT NULL, --|   уникальная комбинация
    F_B  SMALLINT NOT NULL, --|-> (она могла бы быть первичным ключом,
    F_C  SMALLINT NOT NULL, --|   но так как это слишком длинно для связи с подчин. табл.,
    F_D  SMALLINT NOT NULL, --|   то я добавила поле F_PK)

    F1  SMALLINT NOT NULL,  --|-> зависят от ПК
    F2  SMALLINT NOT NULL); --|

Поля F_PK, F_V во вью скрыты, остальные видны.
У модели стоит фильтр F_V = 0.
Алгоритм предусматривает, что оператор может редактировать поля F_A, F_B, F_C, F_D, F1, F2.

--TABLE T_OBJ_FR может содержать 0...20 значений F_FR для каждого TO_F_PK
CREATE TABLE T_OBJ_FR (
    F_RECORD_NMB INTEGER NOT NULL, --первичный ключ, генератор
    TO_F_PK      INTEGER NOT NULL, --внешний ключ, ссылается на поле F_PK таблицы T_OBJ
    F_FR         DOUBLE NOT NULL
);

В таблице T_OBJ можно "безобидно" отредактировать только поля F1, F2.
А поля F_A, F_B, F_C, F_D надо редактировать только в совокупности.
Например, в таблице есть записи:
F_PK=1 F_A=1 F_B=1 F_C=1 F_D=1 F1=1 F2=1
F_PK=2 F_A=2 F_B=1 F_C=1 F_D=1 F1=1 F2=1

Если в записи с F_PK=1 надо поменять F_A=1 F_B=1 на F_A=2 F_B=2, то нельзя после изменения F_A с 1 на 2 сразу подтвердить это частичное изменение записи, а потом поменять F_B с 1 на 2 и подтвердить изменение. Иначе нарушится уникальность комбинации полей F_A, F_B, F_C, F_D в таблице.

После редактирования всех полей записи, которые надо поменять, надо как-то сообщить программе, что оператор закончил редактирование конкретной записи. Тогда программа должна проверить, правомерно ли такое редактирование записи (не нарушает ли оно уникальную комбинацию полей), если правомерно - то подтвердить редактирование и запомнить изменения в БД. К тому же, программа должна в соответствии с тем, какие из полей F_A, F_B, F_C, F_D поменяли свои значения, произвести дополнительное программное редактирование некоторых таблиц БД.

Как сделать редактирование таких таблиц прямо в QTableView, я не понимаю...
К сожалению, никогда не видела подобного работающего примера.
Мешает уникальная комбинация полей. Или я не так спроектировала структуру таблиц?

И еще, надо ли делать уникальный ключ для группы полей F_A, F_B, F_C, F_D? С ключом будет лучше?

Может быть, сделать так...
Сделать одну панель с тремя кнопками (Добавить, Изменить, Удалить).
Слева на экране отобразить блокнот с таблицами, справа выделить область для редактирования данных.
При нажатии кнопки Изменить, начать редактирование текущей записи таблицы, которая перед этим была в фокусе. Для этого в области справа отобразить, в соответствии с типом поля, разные LineEdit, ComboBox, SpinBox, в которые занести данные из текущей записи.
После редактирования всех полей записи, которые надо поменять, нажать кнопку "Редактирование записи завершено", при этом проверить уникальность, если все хорошо, то запомнить изменения в БД, произвести дополнительное программное редактирование некоторых таблиц БД.
Вопрос: эти LineEdit, ComboBox, SpinBox можно добавлять к мапперу QDataWidgetMapper? Или нельзя из-за уникальной комбинации полей?

Есть еще вариант - вместо выделения области для редактирования в основном окне открывать окно редактирования текущей записи как диалог...

Вообще-то, вариант редактирования записей в другой области экрана, а не в самом TableView, мне не нравится вот чем.
Мне кажется, надо делать так, чтобы оператор редактировал все таблицы БД по одному алгоритму. (А не так, что одни таблицы редактируюся в отдельной области, а другие в самом вью.)
А в большинстве подчиненных таблиц у меня редактируется только одно поле (например, в таблице T_OBJ_FR).
Не хочется для ввода одного значения заставлять оператора переключаться в область редактирования справа...
Но, зато этот вариант мне кажется намного проще запрограммировать.

Спасибо :)

Сообщение отредактировал Steklova Olga - 29.5.2013, 8:16
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Litkevich Yuriy
  опции профиля:
сообщение 28.5.2013, 19:17
Сообщение #2


разработчик РЭА
*******

Группа: Сомодератор
Сообщений: 9669
Регистрация: 9.1.2008
Из: Тюмень
Пользователь №: 64

Спасибо сказали: 807 раз(а)




Репутация:   94  


Цитата(Steklova Olga @ 28.5.2013, 18:48) *
И еще, надо ли делать уникальный ключ для группы полей F_A, F_B, F_C, F_D? С ключом будет лучше?
нельзя делать, т.к. оператор может захотеть (понадобится) изменить две записи на противоположные, при фиксации изменений в БД, БД будет "позаписьно" пытаться применить изменения - возникнет конфликт, транзакцию завершить не удастся.


Фиксировать данные в БД нужно вручную (ManualSubmit).
на модель можно вешать "тригеры" (например, beforeUpdate)
в "тригерах" и обрабатывай проверку на уникальность.

Правда тут нужно подумать что делать если условие не соблюдено.
Самое простое - вернуть всё назад, но это начнёт бесить оператора (его ошибка может быть очень маленькой, а изменения которые ему придётся повторить - очень большие).
Сложнее - сигналить из модели об ошибке, показывать суть ошибки оператору, принудительно переводить делегата (одного из) в режим редактирования.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 29.5.2013, 7:29
Сообщение #3


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Цитата(Litkevich Yuriy @ 28.5.2013, 22:17) *
это начнёт бесить оператора (его ошибка может быть очень маленькой, а изменения которые ему придётся повторить - очень большие).

в этом и есть проблема. пользователь делает всё последовательно. он не может менять две записи параллельно. сначала он правит одну, потом - другую. в момент где-то посередине изменений текущее состояние может не соответствовать уникальности. но если начать сразу предпринимать какие-то действия, то юзера это точно будет бесить. лучше так не делать.
проверять нужно перед завершением транзакции, вручную. и предупреждать пользователя об ошибках, а не откатывать сразу всё.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Steklova Olga
  опции профиля:
сообщение 29.5.2013, 9:40
Сообщение #4


Участник
**

Группа: Участник
Сообщений: 198
Регистрация: 27.9.2011
Из: Санкт-Петербург
Пользователь №: 2912

Спасибо сказали: 5 раз(а)




Репутация:   4  


Цитата(Litkevich Yuriy)
оператор может захотеть (понадобится) изменить две записи на противоположные

Я собиралась вносить изменения в БД сразу после редактирования очередной записи, но еще не придумала, какие варианты решения предлагать оператору, если возникнет нарушение уникальности.
И не собиралась разрешать оператору изменять две записи на противоположные.

Хотя, наверное, я соглашусь с вами, что не надо издеваться над оператором, надо разрешить менять несколько записей, как ему захочется, и только после этого нажимать кнопку подтверждения внесенных изменений. Главное, чтобы оператор не забывал ее нажимать.

А в целом мне остается совершенно неясно, как запрограммировать корректировку таблиц.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Алексей1153
  опции профиля:
сообщение 29.5.2013, 10:47
Сообщение #5


фрилансер
******

Группа: Участник
Сообщений: 2939
Регистрация: 19.6.2010
Из: Обливион
Пользователь №: 1822

Спасибо сказали: 215 раз(а)




Репутация:   34  


я бы при щелчке (двойном?) по строке открыл бы диалог редактирования записи. Юзер подправил, нажал, и, если что, ничего не сотрётся, просто предупреждение покажется, а диалог не закроется. И всегда можно отмену нажать. После подтверждения данные собираются и записываются
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Steklova Olga
  опции профиля:
сообщение 29.5.2013, 11:16
Сообщение #6


Участник
**

Группа: Участник
Сообщений: 198
Регистрация: 27.9.2011
Из: Санкт-Петербург
Пользователь №: 2912

Спасибо сказали: 5 раз(а)




Репутация:   4  


Цитата(Алексей1153 @ 29.5.2013, 10:47) *
открыл бы диалог редактирования записи

В этом диалоге будут LineEdit, ComboBox, SpinBox...
Модель у меня наследуется от QSqlRelationalTableModel.
Цитата(Steklova Olga @ 28.5.2013, 16:48) *
Вопрос: эти LineEdit, ComboBox, SpinBox можно (нужно) добавлять к мапперу QDataWidgetMapper?

Как поместить данные из текущей записи в эти LineEdit, ComboBox, SpinBox? С помощью маппера?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Алексей1153
  опции профиля:
сообщение 29.5.2013, 11:19
Сообщение #7


фрилансер
******

Группа: Участник
Сообщений: 2939
Регистрация: 19.6.2010
Из: Обливион
Пользователь №: 1822

Спасибо сказали: 215 раз(а)




Репутация:   34  


да вручную поместить всё в конструкторе диалога ) В конструктор передать текущее содержимое строки. Откорректированное содержимое хранить в самом диалоге, а по итогу того, что вернётся из exec() - либо забыть всё, либо вытащить и записать в БД
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Steklova Olga
  опции профиля:
сообщение 29.5.2013, 11:29
Сообщение #8


Участник
**

Группа: Участник
Сообщений: 198
Регистрация: 27.9.2011
Из: Санкт-Петербург
Пользователь №: 2912

Спасибо сказали: 5 раз(а)




Репутация:   4  


Алексей,
ComboBox'ы для связанных полей тоже вручную заполнять в конструкторе диалога?
Или сделать это как-то с помощью маппера?

А какой должен быть тип переменной
- для передачи содержимого текущей записи в диалог?
- для хранения в классе диалога откорректированного содержимого?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Алексей1153
  опции профиля:
сообщение 29.5.2013, 11:54
Сообщение #9


фрилансер
******

Группа: Участник
Сообщений: 2939
Регистрация: 19.6.2010
Из: Обливион
Пользователь №: 1822

Спасибо сказали: 215 раз(а)




Репутация:   34  


Steklova Olga, там для комбобокса, вроде, тоже модель можно установить. Но я никогда это не делал, обычно std::map в помощь (но у меня длинных списков не было)

тип - какой удобнее. Можно вообще создать экземпляр диалога, потом в его ui всё раскидать снаружи, после чего открыть. И так же после закрытия всё собрать.

Диалог должен как можно меньше знать о том, что происходит снаружи него :) А про БД он вообще знать ничего не должен
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Steklova Olga
  опции профиля:
сообщение 29.5.2013, 12:24
Сообщение #10


Участник
**

Группа: Участник
Сообщений: 198
Регистрация: 27.9.2011
Из: Санкт-Петербург
Пользователь №: 2912

Спасибо сказали: 5 раз(а)




Репутация:   4  


Цитата(Steklova Olga @ 29.5.2013, 11:29) *
А какой должен быть тип переменной
- для передачи содержимого текущей записи в диалог?
- для хранения в классе диалога откорректированного содержимого?

QSqlRecord?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

3 страниц V   1 2 3 >
Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 28.4.2024, 3:30