Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: QSqlTableModel и метод setQuery QSqlQueryModel *q=dynamic_cast<QSqlQueryModel*>(model)
Форум на CrossPlatform.RU > Библиотеки > Qt > Qt Модель/Представление
Max Payne
Задача сделать редактирование данных с разных таблиц в одном QTableView. Нашел как это делается через наследование :
QSqlTableModel *model = new QSqlTableModel(this,dbSql);
QSqlQueryModel *queryModel = dynamic_cast<QSqlQueryModel*>(model);


Модель работает если делать не большой запрос типа ("select Name, Id from data where Pol = 1")
Но у меня сложный запрос типа:
select Animals.Код_Породы, Animals.Код_Окраса,Animals.Пол,Animals.Кличка,Animals.Дата_рождения,Animals.Родо
словная,Animals.Отец_Кличка,Animals.Отец_Окрас,Animals.Мать_Кличка,Animals.Мать_О
крас, Заводчик.Заводчики,Владелец.Владелец, View.Expert_1,View.Expert_2, View.NClass_1,View.NClass_2, View.ZOc_1,View.ZOc_2 , View.Nom_Bis_1 , View.Nom_Bis_2, View.Present_1 ,View.Present_2 , View.WCF_Master, View.WCF_Ring, View.BreedShow, View.Paying, View.AddiNote, View.Selling, View.Titul_1,View.Title_1, View.Titul_2, View.Title_2 , View.NB_1, View.NB_2 from Animals, Заводчик,Владелец , View where Заводчик.ID_Breeder=Animals.ID_Breeder And Владелец.ID_owner=Animals.ID_Owner AND View.ID=(select ID from Data where Name='привт') And View.ID_Cat=Animals.ID_Cat And View.AddiList='true'


и выборку корректно делает только по нескольким переменным (у меня это 10 колонок ), а дальше таблица забивается словом, которым определён хидер таблицы. Может кто то с этим сталкивался хелп.
Заранее спасибо.
Steklova Olga
Max Payne,
Цитата
Модель работает если делать небольшой запрос типа ("select Name, Id from data where Pol = 1")
Так тут запрос к одной таблице.

Цитата
Но у меня сложный запрос...
и выборку корректно делает только по нескольким переменным (у меня это 10 колонок )
Как раз 10 полей из одной таблицы и указаны в этом сложном запросе.

QSqlQueryModel Class Reference
The model is read-only by default.
To make it read-write, you must subclass it and reimplement setData() and flags().
Another option is to use QSqlTableModel, which provides a read-write model based on a single database table.

Посмотрите, например, пример в главе 18 книги "Qt на примерах" Юрия Земскова, там как раз написано, как это делать :)
Max Payne
Цитата
Max Payne,
Цитата
Модель работает если делать небольшой запрос типа ("select Name, Id from data where Pol = 1")
Так тут запрос к одной таблице.[/quote
я имел ввиду что модель работает с простыми запросами, где меньше 10 колонок.

Цитата
Цитата
Но у меня сложный запрос...
и выборку корректно делает только по нескольким переменным (у меня это 10 колонок )
Как раз 10 полей из одной таблицы и указаны в этом сложном запросе.

Опять же если доверится элементарной математике и посчитать то там окажется 34 колонки из которых отображается только 10.


Цитата
QSqlQueryModel Class Reference
The model is read-only by default.
To make it read-write, you must subclass it and reimplement setData() and flags().
Another option is to use QSqlTableModel, which provides a read-write model based on a single database table.


Если вы вдумались в то что я там написал, то там ясно , что с помощью наследования и такой фичи как dynamic_cast , я В QSqlTableModel устанавливаю запрос через наследника QSqlQueryModel.
Steklova Olga
Max Payne,
Цитата
я имел ввиду что модель работает с простыми запросами, где меньше 10 колонок.
И в таких простых запросах селектируются поля из разных таблиц?
Приведите пример такого простого запроса, с которым эта модель работает.

Цитата
Опять же если доверится элементарной математике и посчитать то там окажется 34 колонки из которых отображается только 10.
Первые 10 или какие?

PS Я умею считать даже до 35!

Цитата
Как раз 10 полей из одной таблицы и указаны в этом сложном запросе.
Я имела в виду, что 10 полей из одной таблицы и указаны в начале этого сложного запроса.

Steklova Olga
Max Payne,
Цитата
Нашел как это делается через наследование:
QSqlTableModel *model = new QSqlTableModel(this, dbSql);
QSqlQueryModel *queryModel = dynamic_cast<QSqlQueryModel*>(model);

Я тоже нашла описание использования dynamic_cast у Бланшет:
A *obj = new B;
B *b = dynamic_cast<B *>(obj);

Замечаете разницу?

Пардон, Max Payne, я перепутала родителя с наследником.
PAFOS
QSqlTableModel *model = new QSqlTableModel(this,dbSql);
QSqlQueryModel *queryModel = dynamic_cast<QSqlQueryModel*>(model);


тут вроде все в порядке...


возможно проблема в подзапросах - попробуй без них.
Max Payne
Цитата(PAFOS @ 6.4.2012, 15:47) *
QSqlTableModel *model = new QSqlTableModel(this,dbSql);
QSqlQueryModel *queryModel = dynamic_cast<QSqlQueryModel*>(model);


тут вроде все в порядке...


возможно проблема в подзапросах - попробуй без них.


Таки нашел :
Цитата
Detailed Description
The QSqlTableModel class provides an editable data model for a single database table

наследование QSqlQueryModel никак не исправляет это =/
Может есть выход как сделать редактирование нескольких таблиц одновременно? Или может кто то уже реализовывал свою редактируемую модель?
Steklova Olga
Max Payne, повторюсь,
To make QSqlQueryModel read-write, you must subclass it and reimplement setData() and flags().
И все получится!

И пример нужный есть в книге "Qt на примерах" Юрия Земскова. Вот тут, например, можно скачать.

А от подзапросов можно избавиться, использовав INNER JOIN.

Цитата
Таки нашел:
Detailed Description
The QSqlTableModel class provides an editable data model for a single database table
А прочитать полностью мой первый ответ?...
Max Payne
Цитата(Steklova Olga @ 6.4.2012, 17:25) *
Max Payne, повторюсь,
To make QSqlQueryModel read-write, you must subclass it and reimplement setData() and flags().
И все получится!

И пример нужный есть в книге "Qt на примерах" Юрия Земскова. Вот тут, например, можно скачать.

А от подзапросов можно избавиться, использовав INNER JOIN.

Цитата
Таки нашел:
Detailed Description
The QSqlTableModel class provides an editable data model for a single database table
А прочитать полностью мой первый ответ?...


Спасибо большое, пошел читать мануал =)
Steklova Olga
Цитата(PAFOS @ 6.4.2012, 15:47) *
QSqlTableModel *model = new QSqlTableModel(this,dbSql);
QSqlQueryModel *queryModel = dynamic_cast<QSqlQueryModel*>(model);
тут вроде все в порядке...
Эти два указателя описаны верно. Но дальше они неверно используются, когда в запросе указываются поля из разных таблиц.

Max Payne,
использование QSqlRelationalTableModel, которое Вам посоветовали на prog.org, тоже не подойдет, по той же причине, что и использование QSqlTableModel

Цитата(Max Payne @ 6.4.2012, 16:49) *
Цитата
Detailed Description: The QSqlTableModel class provides an editable data model for a single database table
наследование QSqlQueryModel никак не исправляет это =/
Не мешайте в кучу dynamic_cast, QSqlTableModel и QSqlQueryModel.
Предлагаю для Вашего примера
1. забыть пока про dynamic_cast
2. забыть пока про QSqlTableModel
3. создать класс-наследник от класса QSqlQueryModel, указать в этом классе-наследнике, что данные в модели будут редактируемыми, переопределить в этом классе все необходимое для редактирования.

Max Payne, у Вас некорректно написано одно из условий в запросе. Вот это:
Цитата(Max Payne @ 6.4.2012, 10:20) *
View.ID = (select ID from Data where Name='привт')
Вы тут сравниваете на равенство
одиночное значение
(View.ID)
с множеством значений, которое может выдать подзапрос (select ID from Data where Name='привт').
Ваша логика тут не понятна.
И, вообще, лучше так не писать. Может быть, в отдельной ситуации подзапрос и выдаст только одно значение,
но если данные в БД потом изменятся или дополнятся, то запрос может оказаться ошибочным.
Вашего алгоритма я не знаю, но я поменяла бы это условие на один из следующих вариантов (на какой - зависит от алгоритма):
View.ID IN (select ID from Data where Name='привт')
View.ID = (select FIRST 1 ID from Data where Name='привт')

Max Payne, пожалуйста, прочитайте, наконец, правила оформления сообщений :),
проанализируйте, что получается при нажатии разных кнопок, предназначенных для этого (не нажимая при этом кнопки "Отправить"),
используйте кнопку "Предварительный просмотр", если пишите не быстрый ответ,
а главное, не цитируйте Вы целыми сообщениями, это занимает много места и усложняет чтение.
А когда Вам задают вопросы, хорошо бы на них отвечать, а не игнорировать.

Успехов !
Max Payne
Steklova Olga, я вроде бы стараюсь перечитывать сообщения перед отправкой, извините если пишу не корректно .
QSqlTableModel *model = new QSqlTableModel(this,dbSql);
QSqlQueryModel *queryModel = dynamic_cast<QSqlQueryModel*>(model)

Цитата
Эти два указателя описаны верно. Но дальше они неверно используются, когда в запросе указываются поля из разных таблиц.

Я это понял уже, просто когда я нашел этот пример я надеялся что наследование поможет, но потом понял что ограничение в самой модели QSqlTableModel.

View.ID = (select ID from Data where Name='привт')

Запрос вроде правильный, так как при установке его в QSqlQueryModel, все корректно отображается. И сам он работает как цикл внутри цикла, сначала идет выборка всех ID from Date, а потом каждый такой ID приравнивается к View.ID и в итоге получаем правильный запрос. ТО что это не правильно в общем я согласен, просто быстрое решение первое нашел и попробовал.

Воспользовавшись вашим советом, попробовал создать редактированный наследник от QSqlQueryModel по примеру с книги автора Земской. Но возникли проблемы.
Переопределив setData, я получил ошибку
QSqlQuery::prepare: database not open
"Driver not loaded Driver not loaded"

при попытке обновления данных , привожу код:
объявление
class model : public QSqlQueryModel
{
    Q_OBJECT
    public:
        explicit model(QObject *parent = 0);
        Qt::ItemFlags flags(const QModelIndex &index) const;
        QVariant data(const QModelIndex &index,int role = Qt::DisplayRole) const;
        bool setData(const QModelIndex &index,const QVariant &value, int role);
    private:
        void refresh();
};


И реализация
model::model(QObject *parent):QSqlQueryModel(parent)
{
    refresh();
}
void model::refresh()
{
       return;
}

Qt::ItemFlags model::flags(const QModelIndex &index) const
{
    Qt::ItemFlags flags = QSqlQueryModel::flags(index);
    if (index.column() >= 1 && index.column() < 4)
        flags |= Qt::ItemIsEditable;
    if (index.column() == 4)
        flags |= Qt::ItemIsUserCheckable;
    return flags;
}

QVariant model::data(const QModelIndex &index,int role) const
{
    QVariant value = QSqlQueryModel::data(index, role);
     return value;
}

bool model::setData(const QModelIndex &index,const QVariant &value,int /* role */)
{
    if (index.column() < 1 || index.column() > 4)
        return false;
    QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(), 0);
    int id = QSqlQueryModel::data(primaryKeyIndex).toInt();
    bool ok;
    QSqlQuery query;
    if ((index.column()==1))    // я просто попытался сделать хоть одно редактируемое поле
    {
        query.prepare("update Animals set Код_Окраса=? where ID_Cat=?");
        query.addBindValue(value);
        query.addBindValue(id);
    }

    if(query.exec()== false) qDebug() << query.lastError().text();   // тут выкидывает ошибку что база не открыта
    refresh();

}

в мэйн классе я создаю объект наследника и при установке запроса передаю указатель на базу:
model * m = new model(this);
    m->setQuery("тут мой большой запрос , сделал как вы посоветовали ",dbSql);
    ui->tableView_3->setModel(m);
    LineDelegate *dl = new LineDelegate (LineDelegate::TLine_add); // тут мой делегат - редактируемый QLineEdit
    ui->tableView->setItemDelegateForColumn(1,dl);
    return;


Не могу понять в чем ошибка, указатель на базу передается только при установке , а внутри класа модели наследника база не указывается. Объясните пожалуйста . если Вы с таким сталкивались.
Steklova Olga
Max Payne, вот такое сообщение намного читабельнее :)
Единственное, что Ваш "код реализации" (из-за того, что он достаточно длинный) лучше было вложить внутрь тега "раскрывающийся текст".
[ expand=реализация ][ code ]сюда вставляем код[ /code ][ /expand ]
("=реализация" надо вписывать самому, пробелы вставила, чтобы Вам были видны теги).

1.
Цитата(Max Payne @ 8.4.2012, 15:35) *
Запрос вроде правильный
А Вы ПРОВЕРЬТЕ, сработает ли он, если у Вас в БД в таблице Data окажутся записи с такими данными:
ID Name
1 'привт'
2 'привт'
3 'привт'

4 'не привт'
(то есть, несколько записей с Name = 'привт')
Ну и что получилось???

2. Некоторые драйверы БД не поддерживают подготовленные запросы, поэтому я делаю так:
пример
    QString err_db = "";
    if (!db.open()) {
        err_db = "Не удается открыть БД";
        QMessageBox::critical(NULL, "Ошибка", err_db);
        qDebug() << err_db;
        return false;
    }
    QSqlDriver *driver = QSqlDatabase::database().driver();
    if (!driver->hasFeature(QSqlDriver::PreparedQueries)) {
        err_db = "Драйвер БД не поддерживает подготовленные запросы";
        QMessageBox::critical(NULL, "Ошибка", err_db);
        qDebug() << err_db;
        return false;
    }

3. Насчет Вашего кода...
В объявлении класса все ok.
В реализации класса:
model::model(QObject *parent):QSqlQueryModel(parent)
{
    refresh();
}
void model::refresh()
{
       //Хорошенький у Вас refresh! А Вы ничего не хотите в него добавить?
       //тут нужно установить запрос с помощью setQuery,
       //а refresh будет вызываться, как у Вас и указано, в конструкторе модели и в setData после редактирования данных
       return;
}

Qt::ItemFlags model::flags(const QModelIndex &index) const
{
    Qt::ItemFlags flags = QSqlQueryModel::flags(index);

//    if (index.column() >= 1 && index.column() < 4)
//        flags |= Qt::ItemIsEditable;
//    if (index.column() == 4)
//        flags |= Qt::ItemIsUserCheckable; //Если Вам это надо будет потом,
//        //то в model::data надо будет переопределить Qt::CheckStateRole для этой колонки
    //раз собрались редактировать только 1-ю колонку, то так и указывайте
    if (index.column() == 1)
        flags |= Qt::ItemIsEditable;

    return flags;
}

QVariant model::data(const QModelIndex &index,int role) const
{
    QVariant value = QSqlQueryModel::data(index, role);
     return value;
}

bool model::setData(const QModelIndex &index,const QVariant &value,int /* role */)
{
//    if (index.column() < 1 || index.column() > 4)
    //раз собрались редактировать только 1-ю колонку, то так и указывайте
    //иначе будет сделана попытка выполнить неподготовленный запрос
    if (index.column() != 1)
        return false;
    QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(), 0);
    int id = QSqlQueryModel::data(primaryKeyIndex).toInt();
    bool ok;
    QSqlQuery query;
    if ((index.column()==1))    // я просто попытался сделать хоть одно редактируемое поле
    {
        //Вы же только для этой колонки готовите запрос
        query.prepare("update Animals set Код_Окраса=? where ID_Cat=?");
        query.addBindValue(value);
        query.addBindValue(id);
    }
    //а выполнить пытаетесь для колонок 1..4
//    if(query.exec()== false) qDebug() << query.lastError().text();   // тут выкидывает ошибку что база не открыта
//    refresh();
    //надо так (у Земскова смотрите):
    ok = query.exec(); //надо вычислить ok, чтобы его вернуть
    if (!ok) qDebug() << query.lastError().text();
    refresh();
    return ok; //метод setData имеет тип bool, а Вы для редактируемой колонки вообще ничего не возвращали
}
При создании объекта класса:
model * m = new model(this);
    //поклеп на меня, я не советовала тут писать setQuery,
    //как раз тут вызов setQuery - лишний
    //m->setQuery("тут мой большой запрос , сделал как вы посоветовали ",dbSql);
    ui->tableView_3->setModel(m);
    LineDelegate *dl = new LineDelegate (LineDelegate::TLine_add); // тут мой делегат - редактируемый QLineEdit
    ui->tableView->setItemDelegateForColumn(1,dl);
    return;
Max Payne
Спасибо за помощь, ошибки исправил но все равно не помогло; Ласт Ерор выдает
QSqlQuery::exec: database not open

Я не могу понять как модель понимает к какой базе данных ей обращаться, ведь мы не указываем к какой базе подключаться ?
Steklova Olga
Max Payne,
Цитата(Steklova Olga @ 8.4.2012, 14:52) *
когда Вам задают вопросы, хорошо бы на них отвечать, а не игнорировать.

Безответные вопросы:
Цитата(Steklova Olga @ 6.4.2012, 14:26) *
И в таких простых запросах селектируются поля из разных таблиц?
Приведите пример такого простого запроса, с которым эта модель работает.
Цитата(Steklova Olga @ 6.4.2012, 14:26) *
Первые 10 или какие?
Цитата(Steklova Olga @ 9.4.2012, 15:15) *
А Вы ПРОВЕРЬТЕ... Ну и что получилось???
А то я тоже проигнорирую Ваши и пожелаю читать help... Я серьезно.

Теперь насчет последних вопросов...
Цитата(Max Payne @ 8.4.2012, 15:35) *
при установке запроса передаю указатель на базу:
m->setQuery("запрос", dbSql);
Цитата(Max Payne @ 10.4.2012, 2:18) *
QSqlQuery::exec: database not open
Я не могу понять как модель понимает к какой базе данных ей обращаться, ведь мы не указываем к какой базе подключаться ?

Прочтите внимательно Detailed Description в QSqlDatabase Class Reference.
Если вкратце, то там говорится о том, что в простом случае обычно используется defaultconnection к БД,
в таком случае имя соединения к БД не указывается ни в addDatabase, ни в setQuery.
Вы просто делаете так:
//создаете соединение к БД (defaultconnection)
QSqlDatabase db = QSqlDatabase::addDatabase(myDriverName /*здесь нет второго параметра*/);
db.setHostName(myHostName);
db.setDatabaseName(myDatabaseName);
db.setUserName(myUserName);
db.setPassword(myPassword);
//db.setConnectOptions(...);

if (!db.open()) { //открываете соединение с БД
    err_db = "Не удается открыть БД";
    QMessageBox::critical(NULL, "Ошибка", err_db);
    qDebug() << err_db;
    return false;
}

После этого можно будет вызвать
setQuery("запрос" /*здесь нет второго параметра*/)

А несколько соединений к БД нужны в сложном случае, для одновременного выполнения нескольких транзакций.
Используемый драйвер БД должен поддерживать транзакции:
QSqlDriver *driver = QSqlDatabase::database().driver();
if (!driver->hasFeature(QSqlDriver::Transactions)) {
    err_db = "Драйвер БД не поддерживает транзакции";
    QMessageBox::critical(NULL, "Ошибка", err_db);
    qDebug() << err_db;
    return false;
}
Max Payne
Извините виноват ...
Цитата
Ц
И в таких простых запросах селектируются поля из разных таблиц?
Приведите пример такого простого запроса, с которым эта модель работает.

Этот вопрос был когда я пробовал использовать наследование, но это никак не решает проблему, так как даже при наследовании выборку можна сделать только по одной таблице (недостатки QTAbleModel).
Цитата
Первые 10 или какие?

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

Цитата
А Вы ПРОВЕРЬТЕ... Ну и что получилось???

С запросом там вроди нормально все, так как полю Name задано ограничение целосности - Unique, тоесть оно уникально.
Steklova Olga
Max Payne, вот и хорошо, теперь все ясно :)
Max Payne
По поводу соединения по умолчанию тоже понял, всё правильно запрос устанавливается, но все равно не работает...
if (index.column()!=1)// < 1 || index.column() > 4)
        return false;
    QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(), 0);
    int id = QSqlQueryModel::data(primaryKeyIndex).toInt();    // неправильно получаю id, и по этому не работает, обьясните пож. как получается  QModelIndex primaryKeyIndex, это индекс и моей таблицы
    bool ok;                                                                                 //Animals  к которой запрос идет?
    qDebug() <<value;
    QSqlQuery query;
    if ((index.column()==1))
    {
        query.prepare("update Animals set Код_Окраса=? where ID_Cat=?");
        query.addBindValue(value);
        query.addBindValue(id);

    }
       ok = query.exec(); //надо вычислить ok, чтобы его вернуть
       if (!ok) qDebug() << query.lastError().text();
       this->submit();
       refresh();
       return ok;
Steklova Olga
Max Payne, покажите SQL создания Ваших таблиц, в том числе и таблицы Animals.
Приведите в том виде, как у Вас есть.
Чтобы ответить, мне нужно увидеть порядок столбцов (в CREATE TABLE) и что является первичным ключом в каждой таблице.

А у меня (очень приблизительно, потому что я не знаю связей между таблицами) это получилось примерно так (пыталась создать Ваши таблицы в IBExpert, СУБД FireBird 2.1):
мой скрипт (наброски)
/**********************************************************/
/*                  Создание базы данных                  */
/*               MultiTab_CreateDatabase.sql              */
/**********************************************************/
/**/
SET SQL DIALECT 3;

CREATE DATABASE 'D:\MultiTab_Database\MULTITAB.FDB'
USER 'SYSDBA' PASSWORD 'masterkey'
DEFAULT CHARACTER SET WIN1251;
/**/
/*
EXIT;
*/

--##############################################################################

/**********************************************************/
/*       Удаление и повторное создание базы данных        */
/*              MultiTab_RecreateDatabase.sql             */
/**********************************************************/
/*
SET SQL DIALECT 3;

CONNECT 'D:\MultiTab_Database\MULTITAB.FDB'
USER 'SYSDBA' PASSWORD 'masterkey';

DROP DATABASE;


CREATE DATABASE 'D:\MultiTab_Database\MULTITAB.FDB'
USER 'SYSDBA' PASSWORD 'masterkey'
DEFAULT CHARACTER SET WIN1251;
*/
/*
EXIT;
*/

--##############################################################################
/******************************************************************************/
/*                              Создание доменов                              */
/*                         MultiTab_CreateDomains.sql                         */
/******************************************************************************/
/*
SET SQL DIALECT 3;

SET NAMES WIN1251;

CONNECT 'D:\MultiTab_Database\MULTITAB.FDB'
USER 'SYSDBA' PASSWORD 'masterkey';
*/
/******************************************************************************/
/*                                   Домены                                   */
/*                                  Domains                                   */
/******************************************************************************/

CREATE DOMAIN D_INTEGER AS
INTEGER;

CREATE DOMAIN D_TIMESTAMP AS
TIMESTAMP;

CREATE DOMAIN D_VARCHAR_0020 AS
VARCHAR(20) CHARACTER SET WIN1251
COLLATE PXW_CYRL;

COMMIT;

--------------------------------------------------------------------------------
/*
EXIT;
*/

--##############################################################################

/******************************************************************************/
/*                              Создание таблиц                               */
/*                          MultiTab_CreateTables.sql                         */
/******************************************************************************/
/*
SET SQL DIALECT 3;

SET NAMES WIN1251;

CONNECT 'D:\MultiTab_Database\MULTITAB.FDB'
USER 'SYSDBA' PASSWORD 'masterkey';
*/
/******************************************************************************/
/*                              Основные таблицы                              */
/*                                   Tables                                   */
/******************************************************************************/
--------------------------------------------------------------------------------
CREATE TABLE T_ANIMALS (
    ID_ANIMAL            D_INTEGER NOT NULL,
    ID_BREEDER           D_INTEGER NOT NULL, --?
    ID_OWNER             D_INTEGER NOT NULL, --?
    ID_CAT               D_INTEGER NOT NULL, --?
    KOD_PORODA           D_INTEGER,
    KOD_OKRAS            D_INTEGER,
    CHAR_POL             D_VARCHAR_0020,
    CHAR_KLICHKA         D_VARCHAR_0020,
    DATA_ROZD            D_TIMESTAMP,
    CHAR_RODOSL          D_VARCHAR_0020,
    CHAR_KLICHKA_PAPA    D_VARCHAR_0020,
    KOD_OKRAS_PAPA       D_INTEGER,
    CHAR_KLICHKA_MAMA    D_VARCHAR_0020,
    KOD_OKRAS_MAMA       D_INTEGER
);
ALTER TABLE T_ANIMALS ADD CONSTRAINT PK_T_ANIMALS PRIMARY KEY (ID_ANIMAL);
COMMIT;

--------------------------------------------------------------------------------
CREATE TABLE T_BREEDERS (
    ID_BREEDER           D_INTEGER NOT NULL, --?
    CHAR_BREEDER         D_VARCHAR_0020
);
ALTER TABLE T_BREEDERS ADD CONSTRAINT PK_T_BREEDERS PRIMARY KEY (ID_BREEDER);
COMMIT;

--------------------------------------------------------------------------------
CREATE TABLE T_OWNERS (
    ID_OWNER         D_INTEGER NOT NULL,
    CHAR_OWNER       D_VARCHAR_0020
);
ALTER TABLE T_OWNERS ADD CONSTRAINT PK_T_OWNERS PRIMARY KEY (ID_OWNER);
COMMIT;

--------------------------------------------------------------------------------
CREATE TABLE T_VIEW (
    ID                  D_INTEGER NOT NULL,
    ID_CAT              D_INTEGER NOT NULL, --?
    ADDLIST             D_VARCHAR_0020 NOT NULL,
    EXPERT_1            D_INTEGER,
    EXPERT_2            D_INTEGER,
    NCLASS_1            D_INTEGER,
    NCLASS_2            D_INTEGER,
    ZOC_1               D_INTEGER,
    ZOC_2               D_INTEGER,
    NOM_BIS_1           D_INTEGER,
    NOM_BIS_2           D_INTEGER,
    WCF_MASTER          D_INTEGER,
    WCF_RING            D_INTEGER,
    BREEDSHOW           D_INTEGER,
    PAYING              D_INTEGER,
    ADDINOTE            D_INTEGER,
    SELLING             D_INTEGER,
    TITUL_1             D_INTEGER,
    TITLE_1             D_INTEGER,
    TITUL_2             D_INTEGER,
    TITLE_2             D_INTEGER,
    NB_1                D_INTEGER,
    NB_2                D_INTEGER
);
ALTER TABLE T_VIEW ADD CONSTRAINT PK_T_VIEW PRIMARY KEY (ID);
COMMIT;

--------------------------------------------------------------------------------
CREATE TABLE T_DATA (
    ID                   D_INTEGER NOT NULL,
    NAME                 D_VARCHAR_0020 NOT NULL
);
ALTER TABLE T_DATA ADD CONSTRAINT PK_T_DATA PRIMARY KEY (ID);
COMMIT;

--------------------------------------------------------------------------------
/*
EXIT;
*/

--##############################################################################

Max Payne
вот как создается таблица столбец которой я пытаюсь редактировать:
write.exec(" create table Animals (ID_Cat INTEGER PRIMARY KEY NOT NULL,"
                   "Код_Породы VARCHAR (32) NOT NULL,"
                   "Код_Окраса VARCHAR(32) NOT NULL,"
                   "Пол VARCHAR(16) NOT NULL,"
                   "Кличка VARCHAR(128)NOT NULL UNIQUE ,"
                   "Дата_рождения VARCHAR(32) NOT NULL, "
                   "Родословная VARCHAR (64) NOT NULL, "
                   "Отец_Кличка VARCHAR(128), "
                   "Отец_Окрас VARCHAR(32) , "
                   "Мать_Кличка VARCHAR(128)NOT NULL, "
                   "Мать_Окрас VARCHAR(32) NOT NULL, "
                   "ID_Breeder integer NOT NULL, "
                   "ID_Owner integer NOT NULL,"
                   "Гостиница VARCHAR (12),constraint Anim_fk FOREIGN KEY (ID_Cat) references View (ID_Cat) on update cascade on delete cascade)");
Steklova Olga
Max Payne,
1. какая у Вас СУБД?
2. приведите текст запроса, который у Вас сейчас стоит в setQuery.
Max Payne
База данных:
dbSql = QSqlDatabase::addDatabase("QSQLITE","SqlConnection");
    dbSql.setHostName("locallhost");
    dbSql.setDatabaseName("Pan_Kot.db");
    dbSql.setUserName("Max");
    dbSql.setPassword("qwerty");
    QFile qw;
    if (!dbSql.open("Max","qwerty"))
    {
        QMessageBox::critical(this,tr(" Ошибка "),tr("Ошибка при создании базы данных, убедитесь что вы имеете необходимые права для создания файла базы данных : \n ").append(dbSql.lastError().databaseText()));
        return(false);
    }


и запрос:
"select Animals.Код_Породы, Animals.Код_Окраса,Animals.Пол,Animals.Кличка,Animals.Дата_рождения,Animals.Родо
словная,Animals.Отец_Кличка,Animals.Отец_Окрас,Animals.Мать_Кличка,Animals.Мать_О
крас, Заводчик.Заводчики,Владелец.Владелец, View.Expert_1,View.Expert_2, View.NClass_1,View.NClass_2, View.ZOc_1,View.ZOc_2 , View.Nom_Bis_1 , View.Nom_Bis_2, View.Present_1 ,View.Present_2 , View.WCF_Master, View.WCF_Ring, View.BreedShow, View.Paying, View.AddiNote, View.Selling, View.Titul_1,View.Title_1, View.Titul_2, View.Title_2 , View.NB_1, View.NB_2 from Animals, Заводчик,Владелец , View where Заводчик.ID_Breeder=Animals.ID_Breeder And Владелец.ID_owner=Animals.ID_Owner AND View.ID=1 And View.ID_Cat=Animals.ID_Cat And View.AddiList='false'"
Steklova Olga
Еще раз внимательно читаем пример в книге Земскова:
1.
sql.exec( tr(
        "create table employee ( "
        "  id integer PRIMARY KEY, "
        "  name char(30) not null, "
        "  born date null, "
        "  salary numeric(12,2), "
        "  married boolean null ) " ) );
Заметим, что
id - 0-е поле таблицы employee, ПК
name - 1-е поле таблицы employee.

2.
void MyModel::refresh(){
    setQuery("select * from employee...");
    ...
}
Заметим, что запрашиваются все поля таблицы employee, то есть
в 0-й колонке модели окажется employee.id,
в 1-й колонке модели окажется employee.name.

3.
setData у Земского
bool MyModel::setData(const QModelIndex &index,const QVariant &value,int /* role */)
{
    if (index.column() < 1 || index.column() > 4)
        return false;

    //во входном параметре - index той ячейки, которую редактируем,
    //при том, известны index.column() и index.row():
    //index.column() - колонка, которой принадлежит редактируемая ячейка
    //index.row() - запись, которой принадлежит редактируемая ячейка

    //чтобы обновить запись в таблице employee,
    //надо вычислить для редактируемой записи таблицы
    //значения каждого из полей, составляющих первичный ключ
    //(в данном случае в состав ПК входит одно поле, но может быть и несколько)

    //так как в таблице employee ПК - это employee.id (0-е поле),
    //а в setQuery запрашиваются все поля,
    //то мы получаем primaryKeyIndex
    QModelIndex primaryKeyIndex = QSqlQueryModel::index(
            index.row(), //указывая на текущую строку модели и
            0); //указывая на 0-ю колонку модели
    //получаем id, учитывая типа поля
    int id = QSqlQueryModel::data(primaryKeyIndex).toInt();

    bool ok;
    QSqlQuery query;

    if (index.column() == 1) { //заметим, что сюда мы попадаем,
        //когда редактируем 1-ю колонку модели
        query.prepare("update employee set name = ? where id = ?");
        //соответственно и обновляем мы поле name в текущей записи
        query.addBindValue(value.toString());
        query.addBindValue(id);
    }
    ...
    ok = query.exec();
    refresh();
    return ok;
}
4.
а у Вас, Max Payne, в setData:
if (index.column() != 1)
        return false;

//так как Ваш запрос начинается со слов "select Animals.Код_Породы,...",
//то в 0-й колонке модели у Вас Animals.Код_Породы,
//а Вы тут обращаетесь к 0-й колонке модели, чтобы вытащить значение ПК Animals.ID_Cat,
//которого в ней сейчас нет
QModelIndex primaryKeyIndex = QSqlQueryModel::index(
        index.row(), //указываете на текущую строку модели и
        0); //указываете на 0-ю колонку модели
//Код_Породы имеет тип VARCHAR, а Вы его пытаетесь прочесть как целое
int id = QSqlQueryModel::data(primaryKeyIndex).toInt();

//чтобы в модели оказалась колонка с ПК,
//надо включить соотв. поле таблицы в запрос

bool ok;
qDebug() <<value;
QSqlQuery query;
if ((index.column() == 1))
{
    query.prepare("update Animals set Код_Окраса=? where ID_Cat=?");
    query.addBindValue(value);
    query.addBindValue(id);
}
ok = query.exec(); //надо вычислить ok, чтобы его вернуть
if (!ok) qDebug() << query.lastError().text();
this->submit(); //а у Земскова и без submit() работает редактирование
refresh();
return ok;
Max Payne
С этим разобрался большое спасибо =) Еще такой вопрос, с делал модель с пустой строчкой в конце для добавления новой записи и назначил всем колонкам нужные делегаты. Теперь при обычном редактировании любой ячейки, значение с этой ячейки копируется в конец таблицы .
Вот код:

tabM = new QSqlTableModel (this,dbSql);   //модель обьявлена глобально
tabM->setTable(ob);
tabM->setEditStrategy(QSqlTableModel::OnRowChange);
tabM->select();
ui->tableView->setModel(tabM);
tabM->insertRow(tabM->rowCount());

Как мне избежать дублирования данных в конец в пустую строку ? Заранее спасибо .
Steklova Olga
Могу посоветовать следующее.
--------------------------------
Беспл и без рег скачать серьезную книгу по Qt (если у Вас ее еще нет) тут:
http://wmate.ru/ebooks/book533.html
(Qt 4. Программирование GUI на C++ (+ CD) 2-е издание
Автор: Жасмин Бланшет, Марк Саммерфилд
Размер файла: 12.5 мб
Тип файла: DjVu
Язык: Русский)
В одном архиве с книгой там есть все примеры из книги.

Предлагаю посмотреть проект staffmanager.pro.
Текст проекта см в папке Examples | qt-book | chap13 | staffmanager.
Описание проекта см в части II в главе 13 в пунктах "Редактирование данных с использованием форм", "Представление данных в табличной форме".
Обратите внимание на рис. 13.3 на стр. 330 книги, рис. 13.4 на стр. 336 и метод addDepartment на стр. 340.

Там ВСТАВЛЯЮТ НОВУЮ СТРОКУ в конец таблицы, делают эту строку текущей и запускают редактирование нужной ячейки в этой строке ПРИ НАЖАТИИ КНОПКИ "Add Dept". Еще там есть кнопка "Delete Dept".
Мне кажется, и Вам, Max Payne, в проекте надо сделать аналогичные кнопки и их обработку.

--------------------------------
Еще ссылки на книги:
http://wmate.ru/ebooks/search/?search=Qt4
http://ru.wikipedia.org/wiki/Qt
--------------------------------
Может быть, будет полезным этот пример в Qt Creator:
SQL | Music Archive (проект masterdetail.pro)
--------------------------------
P.S. я сама еще не до конца разобралась с проектом staffmanager, так что, давайте учиться вместе :D
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.