3

I have a problem with inserting new QSqlRecord to Postgresql database. Target table is defined as follows:

create table samochod(
  samochod_id integer primary key default nextval('samochod_id_seq'),
  marka varchar(30),
  poj_silnika numeric,
  typ_silnika silnik_typ,
  liczba_osob smallint
);

And code of record insertion is:

  QSqlRecord rec = model->record();

  rec.setGenerated("samochod_id", false);
  rec.setValue("marka", ui.brandEdit->text());
  rec.setValue("poj_silnika", ui.volSpin->value());
  rec.setValue("typ_silnika", ui.engineCombo->currentText());
  rec.setValue("liczba_osob", ui.passengersSpin->value());

  bool ok = model->insertRecord(-1,rec);

All I want to do is insert a new record, and make sql set its samochod_id for me, but despite I set isGenerated to false it crashes with message:

"ERROR:  null value in column "samochod_id" violates not-null constraint
QPSQL: Unable to create query"

What should I do to make QSqlRecord leave generation of samochod_id to database?

vyegorov
  • 21,787
  • 7
  • 59
  • 73
KCH
  • 2,794
  • 2
  • 23
  • 22

5 Answers5

3

You can use removeColumn(int) before QSqlRecord rec = model->record() and it should work. If model is associated to a view (a QTableView instance, for example), you'll be effectively hiding the samochod_id column, though.

Try this:

  // get the index of the id column
  int colId = model.fieldIndex("samochod_id");
  // remove the column from the model
  model.removeColumn(colId);

  QSqlRecord rec = model->record();

  // rec.setGenerated("samochod_id", false); /// not needed anymore
  rec.setValue("marka", ui.brandEdit->text());
  rec.setValue("poj_silnika", ui.volSpin->value());
  rec.setValue("typ_silnika", ui.engineCombo->currentText());
  rec.setValue("liczba_osob", ui.passengersSpin->value());

  bool ok = model->insertRecord(-1,rec);

I can understand why you prefer to insert records this way (avoid writing SQL manually). I think using a QSqlTableModel instance just for the purpose of inserting records is too expensive (models are complex beasts), so I prefer to use a plain QSqlQuery. With this approach it is not necessary to instantiate multiple QSqlTableModels if you need to insert records in multiple tables in a transaction, a single QSqlQuery is enough:

  QSqlDatabase::database("defaultdb").transaction();
  QSqlQuery query(QSqlDatabase::database("defaultdb"));

  query.prepare(QString("INSERT INTO table1 (field1, field2) VALUES(?,?)"));
  query.bindValue(0,aString);
  query.bindValue(1,anotherString);
  query.exec();

  // insert in another table
  query.exec(QString("INSERT INTO table2 (field1, field2) VALUES(...)");

  bool ok = QSqlDatabase::database("defaultdb").commit();
  query.finish();

  if(!ok){
      // do something
  }
dschulz
  • 4,666
  • 1
  • 31
  • 31
2

I have found a weird solution to this problem. Line:

rec.setGenerated("samochod_id", false);

int method above seemed to have no effect, but when I moved it to signal handler of my model, it started to work as expected:

CarsModel::CarsModel(QObject * parent, QSqlDatabase db)
  : SqlModel(parent, db, "samochod")
{
  engineTypes << "Diesel" << "Na gaz" << "Benzynowy" << "Elektryczny";

  connect(this, SIGNAL(beforeInsert(QSqlRecord &)), this,
      SLOT(beforeInsertRec(QSqlRecord &)));
}


void CarsModel::beforeInsertRec(QSqlRecord & rec){
  rec.setGenerated(0, false);
}
KCH
  • 2,794
  • 2
  • 23
  • 22
1

This is a known bug since Qt 4.8:

https://bugreports.qt-project.org/browse/QTBUG-23592

Jason
  • 11
  • 1
1

This is a known bug to Qt. Fixed in Qt 5.

Cayan
  • 452
  • 4
  • 8
0

I have no experience working with Qt, but I would recommend setting no value at all for the samochod_id column.

On SQL level this could look like this:

INSERT INTO samochod (marka, poj_silnika, poj_silnika, liczba_osob)
     VALUES (...);

As you see, I just “ignore” samochod_id column, letting PostgreSQL use the default value.

vyegorov
  • 21,787
  • 7
  • 59
  • 73
  • I would have done it exactly as you say if I didn't use Qt's SQL classes, which generate queries for me. My question was how to make them do what I want, not how to do it by hand myself. – KCH Apr 13 '12 at 20:07
  • Can you comment out `rec.setGenerated("samochod_id", false);` call and see how it goes? – vyegorov Apr 13 '12 at 20:09
  • It's strange but it doesn't change application behaviour. Documentation says leaving this line uncommented should make database generate this field for me. – KCH Apr 13 '12 at 20:12