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?

5 Answers 5

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
  }
Sign up to request clarification or add additional context in comments.

Comments

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);
}

Comments

1

This is a known bug since Qt 4.8:

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

Comments

1

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

Comments

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.

3 Comments

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.
Can you comment out rec.setGenerated("samochod_id", false); call and see how it goes?
It's strange but it doesn't change application behaviour. Documentation says leaving this line uncommented should make database generate this field for me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.