1 [[!meta title="Edit stages"]]
2 [[!meta author="rohieb"]]
3 [[!meta license="GNU LGPL, v2.1 or later"]]
6 This page shows the different edit stages of a 1000-line patch which mostly
7 consisted of white space changes in one single hunk, and which was subsequently
8 split into smaller hunks. See the [[related blog
9 post|../splitting-overly-large-hunks-in-patches]] for an explanation what was
12 The code below originates from the [QDjango][] project, which is released under
13 the terms of the [GNU Lesser General Public License][gnu-lgpl], version 2.1 or
16 [QDjango]: https://code.google.com/p/qdjango/
17 [gnu-lgpl]: http://www.gnu.org/licenses/lgpl.html
21 …with far too much whitespace changes:
24 --- qdjango-0.2.6.orig/src/db/QDjangoMetaModel.cpp 2012-09-09 07:34:12.000000000 +0200
25 +++ qdjango-0.2.6/src/db/QDjangoMetaModel.cpp 2012-09-09 08:35:54.000000000 +0200
27 class QDjangoMetaFieldPrivate : public QSharedData
30 - QDjangoMetaFieldPrivate();
31 + QDjangoMetaFieldPrivate();
35 - QString foreignModel;
40 - QVariant::Type type;
44 + QString foreignModel;
49 + QVariant::Type type;
53 QDjangoMetaFieldPrivate::QDjangoMetaFieldPrivate()
54 - : autoIncrement(false),
59 + : autoIncrement(false),
68 - Constructs a new QDjangoMetaField.
69 + Constructs a new QDjangoMetaField.
71 QDjangoMetaField::QDjangoMetaField()
73 - d = new QDjangoMetaFieldPrivate;
74 + d = new QDjangoMetaFieldPrivate;
78 - Constructs a copy of \a other.
79 + Constructs a copy of \a other.
81 QDjangoMetaField::QDjangoMetaField(const QDjangoMetaField &other)
88 - Destroys the meta field.
89 + Destroys the meta field.
91 QDjangoMetaField::~QDjangoMetaField()
96 - Assigns \a other to this meta field.
97 + Assigns \a other to this meta field.
99 QDjangoMetaField& QDjangoMetaField::operator=(const QDjangoMetaField& other)
108 - Returns the database column for this meta field.
109 + Returns the database column for this meta field.
111 QString QDjangoMetaField::column() const
113 - return d->db_column;
114 + return d->db_column;
118 - Returns true if this is a valid field.
119 + Returns true if this is a valid field.
121 bool QDjangoMetaField::isValid() const
123 - return !d->name.isEmpty();
124 + return !d->name.isEmpty();
128 - Returns name of this meta field.
129 + Returns name of this meta field.
131 QString QDjangoMetaField::name() const
133 - return QString::fromLatin1(d->name);
134 + return QString::fromLatin1(d->name);
138 - Transforms the given field value for database storage.
139 + Transforms the given field value for database storage.
141 QVariant QDjangoMetaField::toDatabase(const QVariant &value) const
143 - if (d->type == QVariant::String && !d->null && value.isNull())
144 - return QString("");
145 - else if (!d->foreignModel.isEmpty() && d->type == QVariant::Int && d->null && !value.toInt()) {
146 - // store 0 foreign key as NULL if the field is NULL
150 + if (d->type == QVariant::String && !d->null && value.isNull())
151 + return QString("");
152 + else if (!d->foreignModel.isEmpty() && d->type == QVariant::Int && d->null && !value.toInt()) {
153 + // store 0 foreign key as NULL if the field is NULL
159 static QMap<QString, QString> parseOptions(const char *value)
161 - QMap<QString, QString> options;
162 - QStringList items = QString::fromUtf8(value).split(' ');
163 - foreach (const QString &item, items) {
164 - QStringList assign = item.split('=');
165 - if (assign.size() == 2) {
166 - options[assign[0].toLower()] = assign[1];
168 - qWarning() << "Could not parse option" << item;
172 + QMap<QString, QString> options;
173 + QStringList items = QString::fromUtf8(value).split(' ');
174 + foreach (const QString &item, items) {
175 + QStringList assign = item.split('=');
176 + if (assign.size() == 2) {
177 + options[assign[0].toLower()] = assign[1];
179 + qWarning() << "Could not parse option" << item;
185 class QDjangoMetaModelPrivate : public QSharedData
188 - QList<QDjangoMetaField> localFields;
189 - QMap<QByteArray, QString> foreignFields;
190 - QByteArray primaryKey;
192 + QList<QDjangoMetaField> localFields;
193 + QMap<QByteArray, QString> foreignFields;
194 + QByteArray primaryKey;
199 - Constructs a new QDjangoMetaModel by inspecting the given \a model instance.
200 + Constructs a new QDjangoMetaModel by inspecting the given \a model instance.
202 QDjangoMetaModel::QDjangoMetaModel(const QObject *model)
203 - : d(new QDjangoMetaModelPrivate)
204 + : d(new QDjangoMetaModelPrivate)
211 - const QMetaObject* meta = model->metaObject();
212 - d->table = QString(meta->className()).toLower().toLatin1();
213 + const QMetaObject* meta = model->metaObject();
214 + d->table = QString(meta->className()).toLower().toLatin1();
216 + // parse table options
217 + const int optionsIndex = meta->indexOfClassInfo("__meta__");
218 + if (optionsIndex >= 0) {
219 + QMap<QString, QString> options = parseOptions(meta->classInfo(optionsIndex).value());
220 + QMapIterator<QString, QString> option(options);
221 + while (option.hasNext()) {
223 + if (option.key() == "db_table")
224 + d->table = option.value();
228 + const int count = meta->propertyCount();
229 + for(int i = QObject::staticMetaObject.propertyCount(); i < count; ++i)
231 + QString typeName = meta->property(i).typeName();
232 + if (!qstrcmp(meta->property(i).name(), "pk"))
235 + // parse field options
236 + bool autoIncrementOption = false;
237 + QString dbColumnOption;
238 + bool dbIndexOption = false;
239 + bool ignoreFieldOption = false;
240 + int maxLengthOption = 0;
241 + bool primaryKeyOption = false;
242 + bool nullOption = false;
243 + bool uniqueOption = false;
244 + const int infoIndex = meta->indexOfClassInfo(meta->property(i).name());
245 + if (infoIndex >= 0)
247 + QMap<QString, QString> options = parseOptions(meta->classInfo(infoIndex).value());
248 + QMapIterator<QString, QString> option(options);
249 + while (option.hasNext()) {
251 + const QString value = option.value();
252 + if (option.key() == "auto_increment")
253 + autoIncrementOption = (value.toLower() == "true" || value == "1");
254 + else if (option.key() == "db_column")
255 + dbColumnOption = value;
256 + else if (option.key() == "db_index")
257 + dbIndexOption = (value.toLower() == "true" || value == "1");
258 + else if (option.key() == "ignore_field")
259 + ignoreFieldOption = (value.toLower() == "true" || value == "1");
260 + else if (option.key() == "max_length")
261 + maxLengthOption = value.toInt();
262 + else if (option.key() == "null")
263 + nullOption = (value.toLower() == "true" || value == "1");
264 + else if (option.key() == "primary_key")
265 + primaryKeyOption = (value.toLower() == "true" || value == "1");
266 + else if (option.key() == "unique")
267 + uniqueOption = (value.toLower() == "true" || value == "1");
272 + if (ignoreFieldOption)
276 + if (typeName.endsWith("*"))
278 + const QByteArray fkName = meta->property(i).name();
279 + const QString fkModel = typeName.left(typeName.size() - 1);
280 + d->foreignFields.insert(fkName, fkModel);
282 + QDjangoMetaField field;
283 + field.d->name = fkName + "_id";
284 + // FIXME : the key is not necessarily an INTEGER field, we should
285 + // probably perform a lookup on the foreign model, but are we sure
286 + // it is already registered?
287 + field.d->type = QVariant::Int;
288 + field.d->foreignModel = fkModel;
289 + field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption;
290 + field.d->index = true;
291 + field.d->null = nullOption;
292 + if (primaryKeyOption) {
293 + field.d->autoIncrement = autoIncrementOption;
294 + field.d->unique = true;
295 + d->primaryKey = field.d->name;
296 + } else if (uniqueOption) {
297 + field.d->unique = true;
299 + d->localFields << field;
304 + QDjangoMetaField field;
305 + field.d->name = meta->property(i).name();
306 + field.d->type = meta->property(i).type();
307 + field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption;
308 + field.d->index = dbIndexOption;
309 + field.d->maxLength = maxLengthOption;
310 + field.d->null = nullOption;
311 + if (primaryKeyOption) {
312 + field.d->autoIncrement = autoIncrementOption;
313 + field.d->unique = true;
314 + d->primaryKey = field.d->name;
315 + } else if (uniqueOption) {
316 + field.d->unique = true;
319 + d->localFields << field;
322 + // automatic primary key
323 + if (d->primaryKey.isEmpty()) {
324 + QDjangoMetaField field;
325 + field.d->name = "id";
326 + field.d->type = QVariant::Int;
327 + field.d->db_column = "id";
328 + field.d->autoIncrement = true;
329 + field.d->unique = true;
330 + d->localFields.prepend(field);
331 + d->primaryKey = field.d->name;
334 - // parse table options
335 - const int optionsIndex = meta->indexOfClassInfo("__meta__");
336 - if (optionsIndex >= 0) {
337 - QMap<QString, QString> options = parseOptions(meta->classInfo(optionsIndex).value());
338 - QMapIterator<QString, QString> option(options);
339 - while (option.hasNext()) {
341 - if (option.key() == "db_table")
342 - d->table = option.value();
346 - const int count = meta->propertyCount();
347 - for(int i = QObject::staticMetaObject.propertyCount(); i < count; ++i)
349 - QString typeName = meta->property(i).typeName();
350 - if (!qstrcmp(meta->property(i).name(), "pk"))
353 - // parse field options
354 - bool autoIncrementOption = false;
355 - QString dbColumnOption;
356 - bool dbIndexOption = false;
357 - bool ignoreFieldOption = false;
358 - int maxLengthOption = 0;
359 - bool primaryKeyOption = false;
360 - bool nullOption = false;
361 - bool uniqueOption = false;
362 - const int infoIndex = meta->indexOfClassInfo(meta->property(i).name());
363 - if (infoIndex >= 0)
365 - QMap<QString, QString> options = parseOptions(meta->classInfo(infoIndex).value());
366 - QMapIterator<QString, QString> option(options);
367 - while (option.hasNext()) {
369 - const QString value = option.value();
370 - if (option.key() == "auto_increment")
371 - autoIncrementOption = (value.toLower() == "true" || value == "1");
372 - else if (option.key() == "db_column")
373 - dbColumnOption = value;
374 - else if (option.key() == "db_index")
375 - dbIndexOption = (value.toLower() == "true" || value == "1");
376 - else if (option.key() == "ignore_field")
377 - ignoreFieldOption = (value.toLower() == "true" || value == "1");
378 - else if (option.key() == "max_length")
379 - maxLengthOption = value.toInt();
380 - else if (option.key() == "null")
381 - nullOption = (value.toLower() == "true" || value == "1");
382 - else if (option.key() == "primary_key")
383 - primaryKeyOption = (value.toLower() == "true" || value == "1");
384 - else if (option.key() == "unique")
385 - uniqueOption = (value.toLower() == "true" || value == "1");
390 - if (ignoreFieldOption)
394 - if (typeName.endsWith("*"))
396 - const QByteArray fkName = meta->property(i).name();
397 - const QString fkModel = typeName.left(typeName.size() - 1);
398 - d->foreignFields.insert(fkName, fkModel);
400 - QDjangoMetaField field;
401 - field.d->name = fkName + "_id";
402 - // FIXME : the key is not necessarily an INTEGER field, we should
403 - // probably perform a lookup on the foreign model, but are we sure
404 - // it is already registered?
405 - field.d->type = QVariant::Int;
406 - field.d->foreignModel = fkModel;
407 - field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption;
408 - field.d->index = true;
409 - field.d->null = nullOption;
410 - d->localFields << field;
415 - QDjangoMetaField field;
416 - field.d->name = meta->property(i).name();
417 - field.d->type = meta->property(i).type();
418 - field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption;
419 - field.d->index = dbIndexOption;
420 - field.d->maxLength = maxLengthOption;
421 - field.d->null = nullOption;
422 - if (primaryKeyOption) {
423 - field.d->autoIncrement = autoIncrementOption;
424 - field.d->unique = true;
425 - d->primaryKey = field.d->name;
426 - } else if (uniqueOption) {
427 - field.d->unique = true;
430 - d->localFields << field;
433 - // automatic primary key
434 - if (d->primaryKey.isEmpty()) {
435 - QDjangoMetaField field;
436 - field.d->name = "id";
437 - field.d->type = QVariant::Int;
438 - field.d->db_column = "id";
439 - field.d->autoIncrement = true;
440 - field.d->unique = true;
441 - d->localFields.prepend(field);
442 - d->primaryKey = field.d->name;
448 - Constructs a copy of \a other.
449 + Constructs a copy of \a other.
451 QDjangoMetaModel::QDjangoMetaModel(const QDjangoMetaModel &other)
458 - Destroys the meta model.
459 + Destroys the meta model.
461 QDjangoMetaModel::~QDjangoMetaModel()
466 - Assigns \a other to this meta model.
467 + Assigns \a other to this meta model.
469 QDjangoMetaModel& QDjangoMetaModel::operator=(const QDjangoMetaModel& other)
478 - Creates the database table for this QDjangoMetaModel.
479 + Creates the database table for this QDjangoMetaModel.
481 bool QDjangoMetaModel::createTable() const
483 - QSqlDatabase db = QDjango::database();
484 - QSqlDriver *driver = db.driver();
485 - const QString driverName = db.driverName();
487 - QStringList propSql;
488 - const QString quotedTable = db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName);
489 - foreach (const QDjangoMetaField &field, d->localFields)
491 - QString fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName);
492 - switch (field.d->type) {
493 - case QVariant::Bool:
494 - fieldSql += " BOOLEAN";
496 - case QVariant::ByteArray:
497 - if (driverName == QLatin1String("QPSQL"))
498 - fieldSql += " BYTEA";
500 - fieldSql += " BLOB";
501 - if (field.d->maxLength > 0)
502 - fieldSql += QString("(%1)").arg(field.d->maxLength);
505 - case QVariant::Date:
506 - fieldSql += " DATE";
508 - case QVariant::DateTime:
509 - if (driverName == QLatin1String("QPSQL"))
510 - fieldSql += " TIMESTAMP";
512 - fieldSql += " DATETIME";
514 - case QVariant::Double:
515 - fieldSql += " REAL";
517 - case QVariant::Int:
518 - fieldSql += " INTEGER";
520 - case QVariant::LongLong:
521 - fieldSql += " BIGINT";
523 - case QVariant::String:
524 - if (field.d->maxLength > 0)
525 - fieldSql += QString(" VARCHAR(%1)").arg(field.d->maxLength);
527 - fieldSql += " TEXT";
529 - case QVariant::Time:
530 - fieldSql += " TIME";
533 - qWarning() << "Unhandled type" << field.d->type << "for property" << field.d->name;
537 - if (!field.d->null)
538 - fieldSql += " NOT NULL";
541 - if (field.d->name == d->primaryKey)
542 - fieldSql += " PRIMARY KEY";
544 - // auto-increment is backend specific
545 - if (field.d->autoIncrement) {
546 - if (driverName == QLatin1String("QSQLITE") ||
547 - driverName == QLatin1String("QSQLITE2"))
548 - fieldSql += QLatin1String(" AUTOINCREMENT");
549 - else if (driverName == QLatin1String("QMYSQL"))
550 - fieldSql += QLatin1String(" AUTO_INCREMENT");
551 - else if (driverName == QLatin1String("QPSQL"))
552 - fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName) + " SERIAL PRIMARY KEY";
556 - if (!field.d->foreignModel.isEmpty())
558 - const QDjangoMetaModel foreignMeta = QDjango::metaModel(field.d->foreignModel);
559 - const QDjangoMetaField foreignField = foreignMeta.localField("pk");
560 - fieldSql += QString(" REFERENCES %1 (%2)").arg(
561 - driver->escapeIdentifier(foreignMeta.d->table, QSqlDriver::TableName),
562 - driver->escapeIdentifier(foreignField.column(), QSqlDriver::FieldName));
564 - propSql << fieldSql;
568 - QDjangoQuery createQuery(db);
569 - if (!createQuery.exec(QString("CREATE TABLE %1 (%2)").arg(
571 - propSql.join(", "))))
575 - foreach (const QDjangoMetaField &field, d->localFields) {
576 - if (field.d->index && !field.d->unique) {
577 - const QString indexName = d->table + "_" + field.column();
578 - if (!createQuery.exec(QString("CREATE INDEX %1 ON %2 (%3)").arg(
579 - // FIXME : how should we escape an index name?
580 - driver->escapeIdentifier(indexName, QSqlDriver::FieldName),
582 - driver->escapeIdentifier(field.column(), QSqlDriver::FieldName))))
586 + QSqlDatabase db = QDjango::database();
587 + QSqlDriver *driver = db.driver();
588 + const QString driverName = db.driverName();
590 + QStringList propSql;
591 + const QString quotedTable = db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName);
592 + foreach (const QDjangoMetaField &field, d->localFields)
594 + QString fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName);
595 + switch (field.d->type) {
596 + case QVariant::Bool:
597 + fieldSql += " BOOLEAN";
599 + case QVariant::ByteArray:
600 + if (driverName == QLatin1String("QPSQL"))
601 + fieldSql += " BYTEA";
603 + fieldSql += " BLOB";
604 + if (field.d->maxLength > 0)
605 + fieldSql += QString("(%1)").arg(field.d->maxLength);
608 + case QVariant::Date:
609 + fieldSql += " DATE";
611 + case QVariant::DateTime:
612 + if (driverName == QLatin1String("QPSQL"))
613 + fieldSql += " TIMESTAMP";
615 + fieldSql += " DATETIME";
617 + case QVariant::Double:
618 + fieldSql += " REAL";
620 + case QVariant::Int:
621 + fieldSql += " INTEGER";
623 + case QVariant::LongLong:
624 + fieldSql += " BIGINT";
626 + case QVariant::String:
627 + if (field.d->maxLength > 0)
628 + fieldSql += QString(" VARCHAR(%1)").arg(field.d->maxLength);
630 + fieldSql += " TEXT";
632 + case QVariant::Time:
633 + fieldSql += " TIME";
636 + qWarning() << "Unhandled type" << field.d->type << "for property" << field.d->name;
640 + if (!field.d->null)
641 + fieldSql += " NOT NULL";
644 + if (field.d->name == d->primaryKey)
645 + fieldSql += " PRIMARY KEY";
647 + // auto-increment is backend specific
648 + if (field.d->autoIncrement) {
649 + if (driverName == QLatin1String("QSQLITE") ||
650 + driverName == QLatin1String("QSQLITE2"))
651 + fieldSql += QLatin1String(" AUTOINCREMENT");
652 + else if (driverName == QLatin1String("QMYSQL"))
653 + fieldSql += QLatin1String(" AUTO_INCREMENT");
654 + else if (driverName == QLatin1String("QPSQL"))
655 + fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName) + " SERIAL PRIMARY KEY";
659 + if (!field.d->foreignModel.isEmpty())
661 + const QDjangoMetaModel foreignMeta = QDjango::metaModel(field.d->foreignModel);
662 + const QDjangoMetaField foreignField = foreignMeta.localField("pk");
663 + fieldSql += QString(" REFERENCES %1 (%2)").arg(
664 + driver->escapeIdentifier(foreignMeta.d->table, QSqlDriver::TableName),
665 + driver->escapeIdentifier(foreignField.column(), QSqlDriver::FieldName));
667 + propSql << fieldSql;
671 + QDjangoQuery createQuery(db);
672 + if (!createQuery.exec(QString("CREATE TABLE %1 (%2)").arg(
674 + propSql.join(", "))))
678 + foreach (const QDjangoMetaField &field, d->localFields) {
679 + if (field.d->index && !field.d->unique) {
680 + const QString indexName = d->table + "_" + field.column();
681 + if (!createQuery.exec(QString("CREATE INDEX %1 ON %2 (%3)").arg(
682 + // FIXME : how should we escape an index name?
683 + driver->escapeIdentifier(indexName, QSqlDriver::FieldName),
685 + driver->escapeIdentifier(field.column(), QSqlDriver::FieldName))))
695 - Drops the database table for this QDjangoMetaModel.
696 + Drops the database table for this QDjangoMetaModel.
698 bool QDjangoMetaModel::dropTable() const
700 - QSqlDatabase db = QDjango::database();
701 + QSqlDatabase db = QDjango::database();
703 - QDjangoQuery query(db);
704 - return query.exec(QString("DROP TABLE %1").arg(
705 - db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName)));
706 + QDjangoQuery query(db);
707 + return query.exec(QString("DROP TABLE %1").arg(
708 + db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName)));
712 - Retrieves the QDjangoModel pointed to by the given foreign-key.
713 + Retrieves the QDjangoModel pointed to by the given foreign-key.
720 QObject *QDjangoMetaModel::foreignKey(const QObject *model, const char *name) const
722 - const QByteArray prop(name);
723 - QObject *foreign = model->property(prop + "_ptr").value<QObject*>();
727 - // if the foreign object was not loaded yet, do it now
728 - const QString foreignClass = d->foreignFields[prop];
729 - const QDjangoMetaModel foreignMeta = QDjango::metaModel(foreignClass);
730 - const QVariant foreignPk = model->property(prop + "_id");
731 - if (foreign->property(foreignMeta.primaryKey()) != foreignPk)
733 - QDjangoQuerySetPrivate qs(foreignClass);
734 - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, foreignPk));
736 - if (qs.properties.size() != 1 || !qs.sqlLoad(foreign, 0))
740 + const QByteArray prop(name);
741 + QObject *foreign = model->property(prop + "_ptr").value<QObject*>();
745 + // if the foreign object was not loaded yet, do it now
746 + const QString foreignClass = d->foreignFields[prop];
747 + const QDjangoMetaModel foreignMeta = QDjango::metaModel(foreignClass);
748 + const QVariant foreignPk = model->property(prop + "_id");
749 + if (foreign->property(foreignMeta.primaryKey()) != foreignPk)
751 + QDjangoQuerySetPrivate qs(foreignClass);
752 + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, foreignPk));
754 + if (qs.properties.size() != 1 || !qs.sqlLoad(foreign, 0))
761 - Sets the QDjangoModel pointed to by the given foreign-key.
766 + Sets the QDjangoModel pointed to by the given foreign-key.
772 - \note The \c model will take ownership of the given \c value.
773 + \note The \c model will take ownership of the given \c value.
775 void QDjangoMetaModel::setForeignKey(QObject *model, const char *name, QObject *value) const
777 - const QByteArray prop(name);
778 - QObject *old = model->property(prop + "_ptr").value<QObject*>();
782 - // store the new pointer and update the foreign key
783 - model->setProperty(prop + "_ptr", qVariantFromValue(value));
786 - const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[prop]);
787 - model->setProperty(prop + "_id", value->property(foreignMeta.primaryKey()));
789 - model->setProperty(prop + "_id", QVariant());
791 + const QByteArray prop(name);
792 + QObject *old = model->property(prop + "_ptr").value<QObject*>();
796 + // store the new pointer and update the foreign key
797 + model->setProperty(prop + "_ptr", qVariantFromValue(value));
800 + const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[prop]);
801 + model->setProperty(prop + "_id", value->property(foreignMeta.primaryKey()));
803 + model->setProperty(prop + "_id", QVariant());
808 - Loads the given properties into a \a model instance.
809 + Loads the given properties into a \a model instance.
811 void QDjangoMetaModel::load(QObject *model, const QVariantList &properties, int &pos) const
813 - // process local fields
814 - foreach (const QDjangoMetaField &field, d->localFields)
815 - model->setProperty(field.d->name, properties.at(pos++));
817 - // process foreign fields
818 - if (pos >= properties.size())
820 - foreach (const QByteArray &fkName, d->foreignFields.keys())
822 - QObject *object = model->property(fkName + "_ptr").value<QObject*>();
825 - const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[fkName]);
826 - foreignMeta.load(object, properties, pos);
829 + // process local fields
830 + foreach (const QDjangoMetaField &field, d->localFields)
831 + model->setProperty(field.d->name, properties.at(pos++));
833 + // process foreign fields
834 + if (pos >= properties.size())
836 + foreach (const QByteArray &fkName, d->foreignFields.keys())
838 + QObject *object = model->property(fkName + "_ptr").value<QObject*>();
841 + const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[fkName]);
842 + foreignMeta.load(object, properties, pos);
848 - Returns the foreign field mapping.
849 + Returns the foreign field mapping.
851 QMap<QByteArray, QString> QDjangoMetaModel::foreignFields() const
853 - return d->foreignFields;
854 + return d->foreignFields;
858 - Return the local field with the specified \a name.
859 + Return the local field with the specified \a name.
861 QDjangoMetaField QDjangoMetaModel::localField(const QString &name) const
863 - const QString fieldName = (name == "pk") ? d->primaryKey : name;
864 - foreach (const QDjangoMetaField &field, d->localFields) {
865 - if (field.d->name == fieldName)
868 - return QDjangoMetaField();
869 + const QString fieldName = (name == "pk") ? d->primaryKey : name;
870 + foreach (const QDjangoMetaField &field, d->localFields) {
871 + if (field.d->name == fieldName)
874 + return QDjangoMetaField();
878 - Returns the list of local fields.
879 + Returns the list of local fields.
881 QList<QDjangoMetaField> QDjangoMetaModel::localFields() const
883 - return d->localFields;
884 + return d->localFields;
888 - Returns the name of the primary key for the current QDjangoMetaModel.
889 + Returns the name of the primary key for the current QDjangoMetaModel.
891 QByteArray QDjangoMetaModel::primaryKey() const
893 - return d->primaryKey;
894 + return d->primaryKey;
898 - Returns the name of the database table.
899 + Returns the name of the database table.
901 QString QDjangoMetaModel::table() const
908 - Removes the given \a model instance from the database.
909 + Removes the given \a model instance from the database.
911 bool QDjangoMetaModel::remove(QObject *model) const
913 - const QVariant pk = model->property(d->primaryKey);
914 - QDjangoQuerySetPrivate qs(model->metaObject()->className());
915 - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk));
916 - return qs.sqlDelete();
917 + const QVariant pk = model->property(d->primaryKey);
918 + QDjangoQuerySetPrivate qs(model->metaObject()->className());
919 + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk));
920 + return qs.sqlDelete();
924 - Saves the given \a model instance to the database.
925 + Saves the given \a model instance to the database.
927 - \return true if saving succeeded, false otherwise
928 + \return true if saving succeeded, false otherwise
930 bool QDjangoMetaModel::save(QObject *model) const
932 - // find primary key
933 - const QDjangoMetaField primaryKey = localField("pk");
934 - const QVariant pk = model->property(d->primaryKey);
935 - if (!pk.isNull() && !(primaryKey.d->type == QVariant::Int && !pk.toInt()))
937 - QSqlDatabase db = QDjango::database();
938 - QDjangoQuery query(db);
939 - query.prepare(QString("SELECT 1 AS a FROM %1 WHERE %2 = ?").arg(
940 - db.driver()->escapeIdentifier(d->table, QSqlDriver::FieldName),
941 - db.driver()->escapeIdentifier(primaryKey.column(), QSqlDriver::FieldName)));
942 - query.addBindValue(pk);
943 - if (query.exec() && query.next())
946 - QVariantMap fields;
947 - foreach (const QDjangoMetaField &field, d->localFields) {
948 - if (field.d->name != d->primaryKey) {
949 - const QVariant value = model->property(field.d->name);
950 - fields.insert(field.d->name, field.toDatabase(value));
955 - QDjangoQuerySetPrivate qs(model->metaObject()->className());
956 - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk));
957 - return qs.sqlUpdate(fields) != -1;
962 - QVariantMap fields;
963 - foreach (const QDjangoMetaField &field, d->localFields) {
964 - if (!field.d->autoIncrement) {
965 - const QVariant value = model->property(field.d->name);
966 - fields.insert(field.name(), field.toDatabase(value));
972 - QDjangoQuerySetPrivate qs(model->metaObject()->className());
973 - if (!qs.sqlInsert(fields, &insertId))
976 - // fetch autoincrement pk
977 - if (primaryKey.d->autoIncrement)
978 - model->setProperty(d->primaryKey, insertId);
980 + // find primary key
981 + const QDjangoMetaField primaryKey = localField("pk");
982 + const QVariant pk = model->property(d->primaryKey);
983 + if (!pk.isNull() && !(primaryKey.d->type == QVariant::Int && !pk.toInt()))
985 + QSqlDatabase db = QDjango::database();
986 + QDjangoQuery query(db);
987 + query.prepare(QString("SELECT 1 AS a FROM %1 WHERE %2 = ?").arg(
988 + db.driver()->escapeIdentifier(d->table, QSqlDriver::FieldName),
989 + db.driver()->escapeIdentifier(primaryKey.column(), QSqlDriver::FieldName)));
990 + query.addBindValue(pk);
991 + if (query.exec() && query.next())
994 + QVariantMap fields;
995 + foreach (const QDjangoMetaField &field, d->localFields) {
996 + if (field.d->name != d->primaryKey) {
997 + const QVariant value = model->property(field.d->name);
998 + fields.insert(field.d->name, field.toDatabase(value));
1003 + QDjangoQuerySetPrivate qs(model->metaObject()->className());
1004 + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk));
1005 + return qs.sqlUpdate(fields) != -1;
1010 + QVariantMap fields;
1011 + foreach (const QDjangoMetaField &field, d->localFields) {
1012 + if (!field.d->autoIncrement) {
1013 + const QVariant value = model->property(field.d->name);
1014 + fields.insert(field.name(), field.toDatabase(value));
1019 + QVariant insertId;
1020 + QDjangoQuerySetPrivate qs(model->metaObject()->className());
1021 + if (!qs.sqlInsert(fields, &insertId))
1024 + // fetch autoincrement pk
1025 + if (primaryKey.d->autoIncrement)
1026 + model->setProperty(d->primaryKey, insertId);
1032 ## After editing with Emacs
1034 Note the additional `@@ ... @@` lines:
1036 --- qdjango-0.2.6.orig/src/db/QDjangoMetaModel.cpp 2012-09-09 07:34:12.000000000 +0200
1037 +++ qdjango-0.2.6/src/db/QDjangoMetaModel.cpp 2012-09-09 08:35:54.000000000 +0200
1039 class QDjangoMetaFieldPrivate : public QSharedData
1042 - QDjangoMetaFieldPrivate();
1043 + QDjangoMetaFieldPrivate();
1045 - bool autoIncrement;
1046 - QString db_column;
1047 - QString foreignModel;
1052 - QVariant::Type type;
1054 + bool autoIncrement;
1055 + QString db_column;
1056 + QString foreignModel;
1061 + QVariant::Type type;
1066 QDjangoMetaFieldPrivate::QDjangoMetaFieldPrivate()
1067 - : autoIncrement(false),
1072 + : autoIncrement(false),
1082 - Constructs a new QDjangoMetaField.
1083 + Constructs a new QDjangoMetaField.
1085 QDjangoMetaField::QDjangoMetaField()
1087 - d = new QDjangoMetaFieldPrivate;
1088 + d = new QDjangoMetaFieldPrivate;
1093 - Constructs a copy of \a other.
1094 + Constructs a copy of \a other.
1096 QDjangoMetaField::QDjangoMetaField(const QDjangoMetaField &other)
1104 - Destroys the meta field.
1105 + Destroys the meta field.
1107 QDjangoMetaField::~QDjangoMetaField()
1113 - Assigns \a other to this meta field.
1114 + Assigns \a other to this meta field.
1116 QDjangoMetaField& QDjangoMetaField::operator=(const QDjangoMetaField& other)
1126 - Returns the database column for this meta field.
1127 + Returns the database column for this meta field.
1129 QString QDjangoMetaField::column() const
1131 - return d->db_column;
1132 + return d->db_column;
1137 - Returns true if this is a valid field.
1138 + Returns true if this is a valid field.
1140 bool QDjangoMetaField::isValid() const
1142 - return !d->name.isEmpty();
1143 + return !d->name.isEmpty();
1148 - Returns name of this meta field.
1149 + Returns name of this meta field.
1151 QString QDjangoMetaField::name() const
1153 - return QString::fromLatin1(d->name);
1154 + return QString::fromLatin1(d->name);
1156 @@ -107,14 +107,14 @@
1159 - Transforms the given field value for database storage.
1160 + Transforms the given field value for database storage.
1162 QVariant QDjangoMetaField::toDatabase(const QVariant &value) const
1164 - if (d->type == QVariant::String && !d->null && value.isNull())
1165 - return QString("");
1166 - else if (!d->foreignModel.isEmpty() && d->type == QVariant::Int && d->null && !value.toInt()) {
1167 - // store 0 foreign key as NULL if the field is NULL
1168 - return QVariant();
1171 + if (d->type == QVariant::String && !d->null && value.isNull())
1172 + return QString("");
1173 + else if (!d->foreignModel.isEmpty() && d->type == QVariant::Int && d->null && !value.toInt()) {
1174 + // store 0 foreign key as NULL if the field is NULL
1175 + return QVariant();
1179 @@ -121,15 +121,15 @@
1181 static QMap<QString, QString> parseOptions(const char *value)
1183 - QMap<QString, QString> options;
1184 - QStringList items = QString::fromUtf8(value).split(' ');
1185 - foreach (const QString &item, items) {
1186 - QStringList assign = item.split('=');
1187 - if (assign.size() == 2) {
1188 - options[assign[0].toLower()] = assign[1];
1190 - qWarning() << "Could not parse option" << item;
1194 + QMap<QString, QString> options;
1195 + QStringList items = QString::fromUtf8(value).split(' ');
1196 + foreach (const QString &item, items) {
1197 + QStringList assign = item.split('=');
1198 + if (assign.size() == 2) {
1199 + options[assign[0].toLower()] = assign[1];
1201 + qWarning() << "Could not parse option" << item;
1206 @@ -136,12 +136,12 @@
1208 class QDjangoMetaModelPrivate : public QSharedData
1211 - QList<QDjangoMetaField> localFields;
1212 - QMap<QByteArray, QString> foreignFields;
1213 - QByteArray primaryKey;
1215 + QList<QDjangoMetaField> localFields;
1216 + QMap<QByteArray, QString> foreignFields;
1217 + QByteArray primaryKey;
1222 - Constructs a new QDjangoMetaModel by inspecting the given \a model instance.
1223 + Constructs a new QDjangoMetaModel by inspecting the given \a model instance.
1224 @@ -148,141 +148,148 @@
1226 QDjangoMetaModel::QDjangoMetaModel(const QObject *model)
1227 - : d(new QDjangoMetaModelPrivate)
1228 + : d(new QDjangoMetaModelPrivate)
1235 - const QMetaObject* meta = model->metaObject();
1236 - d->table = QString(meta->className()).toLower().toLatin1();
1237 + const QMetaObject* meta = model->metaObject();
1238 + d->table = QString(meta->className()).toLower().toLatin1();
1240 + // parse table options
1241 + const int optionsIndex = meta->indexOfClassInfo("__meta__");
1242 + if (optionsIndex >= 0) {
1243 + QMap<QString, QString> options = parseOptions(meta->classInfo(optionsIndex).value());
1244 + QMapIterator<QString, QString> option(options);
1245 + while (option.hasNext()) {
1247 + if (option.key() == "db_table")
1248 + d->table = option.value();
1252 + const int count = meta->propertyCount();
1253 + for(int i = QObject::staticMetaObject.propertyCount(); i < count; ++i)
1255 + QString typeName = meta->property(i).typeName();
1256 + if (!qstrcmp(meta->property(i).name(), "pk"))
1259 + // parse field options
1260 + bool autoIncrementOption = false;
1261 + QString dbColumnOption;
1262 + bool dbIndexOption = false;
1263 + bool ignoreFieldOption = false;
1264 + int maxLengthOption = 0;
1265 + bool primaryKeyOption = false;
1266 + bool nullOption = false;
1267 + bool uniqueOption = false;
1268 + const int infoIndex = meta->indexOfClassInfo(meta->property(i).name());
1269 + if (infoIndex >= 0)
1271 + QMap<QString, QString> options = parseOptions(meta->classInfo(infoIndex).value());
1272 + QMapIterator<QString, QString> option(options);
1273 + while (option.hasNext()) {
1275 + const QString value = option.value();
1276 + if (option.key() == "auto_increment")
1277 + autoIncrementOption = (value.toLower() == "true" || value == "1");
1278 + else if (option.key() == "db_column")
1279 + dbColumnOption = value;
1280 + else if (option.key() == "db_index")
1281 + dbIndexOption = (value.toLower() == "true" || value == "1");
1282 + else if (option.key() == "ignore_field")
1283 + ignoreFieldOption = (value.toLower() == "true" || value == "1");
1284 + else if (option.key() == "max_length")
1285 + maxLengthOption = value.toInt();
1286 + else if (option.key() == "null")
1287 + nullOption = (value.toLower() == "true" || value == "1");
1288 + else if (option.key() == "primary_key")
1289 + primaryKeyOption = (value.toLower() == "true" || value == "1");
1290 + else if (option.key() == "unique")
1291 + uniqueOption = (value.toLower() == "true" || value == "1");
1296 + if (ignoreFieldOption)
1300 + if (typeName.endsWith("*"))
1302 + const QByteArray fkName = meta->property(i).name();
1303 + const QString fkModel = typeName.left(typeName.size() - 1);
1304 + d->foreignFields.insert(fkName, fkModel);
1306 + QDjangoMetaField field;
1307 + field.d->name = fkName + "_id";
1308 + // FIXME : the key is not necessarily an INTEGER field, we should
1309 + // probably perform a lookup on the foreign model, but are we sure
1310 + // it is already registered?
1311 + field.d->type = QVariant::Int;
1312 + field.d->foreignModel = fkModel;
1313 + field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption;
1314 + field.d->index = true;
1315 + field.d->null = nullOption;
1316 + if (primaryKeyOption) {
1317 + field.d->autoIncrement = autoIncrementOption;
1318 + field.d->unique = true;
1319 + d->primaryKey = field.d->name;
1320 + } else if (uniqueOption) {
1321 + field.d->unique = true;
1323 + d->localFields << field;
1328 + QDjangoMetaField field;
1329 + field.d->name = meta->property(i).name();
1330 + field.d->type = meta->property(i).type();
1331 + field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption;
1332 + field.d->index = dbIndexOption;
1333 + field.d->maxLength = maxLengthOption;
1334 + field.d->null = nullOption;
1335 + if (primaryKeyOption) {
1336 + field.d->autoIncrement = autoIncrementOption;
1337 + field.d->unique = true;
1338 + d->primaryKey = field.d->name;
1339 + } else if (uniqueOption) {
1340 + field.d->unique = true;
1343 + d->localFields << field;
1346 + // automatic primary key
1347 + if (d->primaryKey.isEmpty()) {
1348 + QDjangoMetaField field;
1349 + field.d->name = "id";
1350 + field.d->type = QVariant::Int;
1351 + field.d->db_column = "id";
1352 + field.d->autoIncrement = true;
1353 + field.d->unique = true;
1354 + d->localFields.prepend(field);
1355 + d->primaryKey = field.d->name;
1358 - // parse table options
1359 - const int optionsIndex = meta->indexOfClassInfo("__meta__");
1360 - if (optionsIndex >= 0) {
1361 - QMap<QString, QString> options = parseOptions(meta->classInfo(optionsIndex).value());
1362 - QMapIterator<QString, QString> option(options);
1363 - while (option.hasNext()) {
1365 - if (option.key() == "db_table")
1366 - d->table = option.value();
1370 - const int count = meta->propertyCount();
1371 - for(int i = QObject::staticMetaObject.propertyCount(); i < count; ++i)
1373 - QString typeName = meta->property(i).typeName();
1374 - if (!qstrcmp(meta->property(i).name(), "pk"))
1377 - // parse field options
1378 - bool autoIncrementOption = false;
1379 - QString dbColumnOption;
1380 - bool dbIndexOption = false;
1381 - bool ignoreFieldOption = false;
1382 - int maxLengthOption = 0;
1383 - bool primaryKeyOption = false;
1384 - bool nullOption = false;
1385 - bool uniqueOption = false;
1386 - const int infoIndex = meta->indexOfClassInfo(meta->property(i).name());
1387 - if (infoIndex >= 0)
1389 - QMap<QString, QString> options = parseOptions(meta->classInfo(infoIndex).value());
1390 - QMapIterator<QString, QString> option(options);
1391 - while (option.hasNext()) {
1393 - const QString value = option.value();
1394 - if (option.key() == "auto_increment")
1395 - autoIncrementOption = (value.toLower() == "true" || value == "1");
1396 - else if (option.key() == "db_column")
1397 - dbColumnOption = value;
1398 - else if (option.key() == "db_index")
1399 - dbIndexOption = (value.toLower() == "true" || value == "1");
1400 - else if (option.key() == "ignore_field")
1401 - ignoreFieldOption = (value.toLower() == "true" || value == "1");
1402 - else if (option.key() == "max_length")
1403 - maxLengthOption = value.toInt();
1404 - else if (option.key() == "null")
1405 - nullOption = (value.toLower() == "true" || value == "1");
1406 - else if (option.key() == "primary_key")
1407 - primaryKeyOption = (value.toLower() == "true" || value == "1");
1408 - else if (option.key() == "unique")
1409 - uniqueOption = (value.toLower() == "true" || value == "1");
1414 - if (ignoreFieldOption)
1418 - if (typeName.endsWith("*"))
1420 - const QByteArray fkName = meta->property(i).name();
1421 - const QString fkModel = typeName.left(typeName.size() - 1);
1422 - d->foreignFields.insert(fkName, fkModel);
1424 - QDjangoMetaField field;
1425 - field.d->name = fkName + "_id";
1426 - // FIXME : the key is not necessarily an INTEGER field, we should
1427 - // probably perform a lookup on the foreign model, but are we sure
1428 - // it is already registered?
1429 - field.d->type = QVariant::Int;
1430 - field.d->foreignModel = fkModel;
1431 - field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption;
1432 - field.d->index = true;
1433 - field.d->null = nullOption;
1434 - d->localFields << field;
1439 - QDjangoMetaField field;
1440 - field.d->name = meta->property(i).name();
1441 - field.d->type = meta->property(i).type();
1442 - field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption;
1443 - field.d->index = dbIndexOption;
1444 - field.d->maxLength = maxLengthOption;
1445 - field.d->null = nullOption;
1446 - if (primaryKeyOption) {
1447 - field.d->autoIncrement = autoIncrementOption;
1448 - field.d->unique = true;
1449 - d->primaryKey = field.d->name;
1450 - } else if (uniqueOption) {
1451 - field.d->unique = true;
1454 - d->localFields << field;
1457 - // automatic primary key
1458 - if (d->primaryKey.isEmpty()) {
1459 - QDjangoMetaField field;
1460 - field.d->name = "id";
1461 - field.d->type = QVariant::Int;
1462 - field.d->db_column = "id";
1463 - field.d->autoIncrement = true;
1464 - field.d->unique = true;
1465 - d->localFields.prepend(field);
1466 - d->primaryKey = field.d->name;
1472 - Constructs a copy of \a other.
1473 + Constructs a copy of \a other.
1475 QDjangoMetaModel::QDjangoMetaModel(const QDjangoMetaModel &other)
1482 - Destroys the meta model.
1483 + Destroys the meta model.
1485 QDjangoMetaModel::~QDjangoMetaModel()
1490 - Assigns \a other to this meta model.
1491 + Assigns \a other to this meta model.
1494 QDjangoMetaModel& QDjangoMetaModel::operator=(const QDjangoMetaModel& other)
1502 @@ -295,112 +302,112 @@
1504 - Creates the database table for this QDjangoMetaModel.
1505 + Creates the database table for this QDjangoMetaModel.
1507 bool QDjangoMetaModel::createTable() const
1509 - QSqlDatabase db = QDjango::database();
1510 - QSqlDriver *driver = db.driver();
1511 - const QString driverName = db.driverName();
1513 - QStringList propSql;
1514 - const QString quotedTable = db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName);
1515 - foreach (const QDjangoMetaField &field, d->localFields)
1517 - QString fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName);
1518 - switch (field.d->type) {
1519 - case QVariant::Bool:
1520 - fieldSql += " BOOLEAN";
1522 - case QVariant::ByteArray:
1523 - if (driverName == QLatin1String("QPSQL"))
1524 - fieldSql += " BYTEA";
1526 - fieldSql += " BLOB";
1527 - if (field.d->maxLength > 0)
1528 - fieldSql += QString("(%1)").arg(field.d->maxLength);
1531 - case QVariant::Date:
1532 - fieldSql += " DATE";
1534 - case QVariant::DateTime:
1535 - if (driverName == QLatin1String("QPSQL"))
1536 - fieldSql += " TIMESTAMP";
1538 - fieldSql += " DATETIME";
1540 - case QVariant::Double:
1541 - fieldSql += " REAL";
1543 - case QVariant::Int:
1544 - fieldSql += " INTEGER";
1546 - case QVariant::LongLong:
1547 - fieldSql += " BIGINT";
1549 - case QVariant::String:
1550 - if (field.d->maxLength > 0)
1551 - fieldSql += QString(" VARCHAR(%1)").arg(field.d->maxLength);
1553 - fieldSql += " TEXT";
1555 - case QVariant::Time:
1556 - fieldSql += " TIME";
1559 - qWarning() << "Unhandled type" << field.d->type << "for property" << field.d->name;
1563 - if (!field.d->null)
1564 - fieldSql += " NOT NULL";
1567 - if (field.d->name == d->primaryKey)
1568 - fieldSql += " PRIMARY KEY";
1570 - // auto-increment is backend specific
1571 - if (field.d->autoIncrement) {
1572 - if (driverName == QLatin1String("QSQLITE") ||
1573 - driverName == QLatin1String("QSQLITE2"))
1574 - fieldSql += QLatin1String(" AUTOINCREMENT");
1575 - else if (driverName == QLatin1String("QMYSQL"))
1576 - fieldSql += QLatin1String(" AUTO_INCREMENT");
1577 - else if (driverName == QLatin1String("QPSQL"))
1578 - fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName) + " SERIAL PRIMARY KEY";
1582 - if (!field.d->foreignModel.isEmpty())
1584 - const QDjangoMetaModel foreignMeta = QDjango::metaModel(field.d->foreignModel);
1585 - const QDjangoMetaField foreignField = foreignMeta.localField("pk");
1586 - fieldSql += QString(" REFERENCES %1 (%2)").arg(
1587 - driver->escapeIdentifier(foreignMeta.d->table, QSqlDriver::TableName),
1588 - driver->escapeIdentifier(foreignField.column(), QSqlDriver::FieldName));
1590 - propSql << fieldSql;
1594 - QDjangoQuery createQuery(db);
1595 - if (!createQuery.exec(QString("CREATE TABLE %1 (%2)").arg(
1597 - propSql.join(", "))))
1601 - foreach (const QDjangoMetaField &field, d->localFields) {
1602 - if (field.d->index && !field.d->unique) {
1603 - const QString indexName = d->table + "_" + field.column();
1604 - if (!createQuery.exec(QString("CREATE INDEX %1 ON %2 (%3)").arg(
1605 - // FIXME : how should we escape an index name?
1606 - driver->escapeIdentifier(indexName, QSqlDriver::FieldName),
1608 - driver->escapeIdentifier(field.column(), QSqlDriver::FieldName))))
1612 + QSqlDatabase db = QDjango::database();
1613 + QSqlDriver *driver = db.driver();
1614 + const QString driverName = db.driverName();
1616 + QStringList propSql;
1617 + const QString quotedTable = db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName);
1618 + foreach (const QDjangoMetaField &field, d->localFields)
1620 + QString fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName);
1621 + switch (field.d->type) {
1622 + case QVariant::Bool:
1623 + fieldSql += " BOOLEAN";
1625 + case QVariant::ByteArray:
1626 + if (driverName == QLatin1String("QPSQL"))
1627 + fieldSql += " BYTEA";
1629 + fieldSql += " BLOB";
1630 + if (field.d->maxLength > 0)
1631 + fieldSql += QString("(%1)").arg(field.d->maxLength);
1634 + case QVariant::Date:
1635 + fieldSql += " DATE";
1637 + case QVariant::DateTime:
1638 + if (driverName == QLatin1String("QPSQL"))
1639 + fieldSql += " TIMESTAMP";
1641 + fieldSql += " DATETIME";
1643 + case QVariant::Double:
1644 + fieldSql += " REAL";
1646 + case QVariant::Int:
1647 + fieldSql += " INTEGER";
1649 + case QVariant::LongLong:
1650 + fieldSql += " BIGINT";
1652 + case QVariant::String:
1653 + if (field.d->maxLength > 0)
1654 + fieldSql += QString(" VARCHAR(%1)").arg(field.d->maxLength);
1656 + fieldSql += " TEXT";
1658 + case QVariant::Time:
1659 + fieldSql += " TIME";
1662 + qWarning() << "Unhandled type" << field.d->type << "for property" << field.d->name;
1666 + if (!field.d->null)
1667 + fieldSql += " NOT NULL";
1670 + if (field.d->name == d->primaryKey)
1671 + fieldSql += " PRIMARY KEY";
1673 + // auto-increment is backend specific
1674 + if (field.d->autoIncrement) {
1675 + if (driverName == QLatin1String("QSQLITE") ||
1676 + driverName == QLatin1String("QSQLITE2"))
1677 + fieldSql += QLatin1String(" AUTOINCREMENT");
1678 + else if (driverName == QLatin1String("QMYSQL"))
1679 + fieldSql += QLatin1String(" AUTO_INCREMENT");
1680 + else if (driverName == QLatin1String("QPSQL"))
1681 + fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName) + " SERIAL PRIMARY KEY";
1685 + if (!field.d->foreignModel.isEmpty())
1687 + const QDjangoMetaModel foreignMeta = QDjango::metaModel(field.d->foreignModel);
1688 + const QDjangoMetaField foreignField = foreignMeta.localField("pk");
1689 + fieldSql += QString(" REFERENCES %1 (%2)").arg(
1690 + driver->escapeIdentifier(foreignMeta.d->table, QSqlDriver::TableName),
1691 + driver->escapeIdentifier(foreignField.column(), QSqlDriver::FieldName));
1693 + propSql << fieldSql;
1697 + QDjangoQuery createQuery(db);
1698 + if (!createQuery.exec(QString("CREATE TABLE %1 (%2)").arg(
1700 + propSql.join(", "))))
1704 + foreach (const QDjangoMetaField &field, d->localFields) {
1705 + if (field.d->index && !field.d->unique) {
1706 + const QString indexName = d->table + "_" + field.column();
1707 + if (!createQuery.exec(QString("CREATE INDEX %1 ON %2 (%3)").arg(
1708 + // FIXME : how should we escape an index name?
1709 + driver->escapeIdentifier(indexName, QSqlDriver::FieldName),
1711 + driver->escapeIdentifier(field.column(), QSqlDriver::FieldName))))
1720 @@ -407,12 +414,12 @@
1722 - Drops the database table for this QDjangoMetaModel.
1723 + Drops the database table for this QDjangoMetaModel.
1725 bool QDjangoMetaModel::dropTable() const
1727 - QSqlDatabase db = QDjango::database();
1728 + QSqlDatabase db = QDjango::database();
1730 - QDjangoQuery query(db);
1731 - return query.exec(QString("DROP TABLE %1").arg(
1732 - db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName)));
1733 + QDjangoQuery query(db);
1734 + return query.exec(QString("DROP TABLE %1").arg(
1735 + db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName)));
1738 @@ -419,27 +426,27 @@
1740 - Retrieves the QDjangoModel pointed to by the given foreign-key.
1741 + Retrieves the QDjangoModel pointed to by the given foreign-key.
1748 QObject *QDjangoMetaModel::foreignKey(const QObject *model, const char *name) const
1750 - const QByteArray prop(name);
1751 - QObject *foreign = model->property(prop + "_ptr").value<QObject*>();
1755 - // if the foreign object was not loaded yet, do it now
1756 - const QString foreignClass = d->foreignFields[prop];
1757 - const QDjangoMetaModel foreignMeta = QDjango::metaModel(foreignClass);
1758 - const QVariant foreignPk = model->property(prop + "_id");
1759 - if (foreign->property(foreignMeta.primaryKey()) != foreignPk)
1761 - QDjangoQuerySetPrivate qs(foreignClass);
1762 - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, foreignPk));
1764 - if (qs.properties.size() != 1 || !qs.sqlLoad(foreign, 0))
1768 + const QByteArray prop(name);
1769 + QObject *foreign = model->property(prop + "_ptr").value<QObject*>();
1773 + // if the foreign object was not loaded yet, do it now
1774 + const QString foreignClass = d->foreignFields[prop];
1775 + const QDjangoMetaModel foreignMeta = QDjango::metaModel(foreignClass);
1776 + const QVariant foreignPk = model->property(prop + "_id");
1777 + if (foreign->property(foreignMeta.primaryKey()) != foreignPk)
1779 + QDjangoQuerySetPrivate qs(foreignClass);
1780 + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, foreignPk));
1782 + if (qs.properties.size() != 1 || !qs.sqlLoad(foreign, 0))
1787 @@ -446,28 +453,28 @@
1790 - Sets the QDjangoModel pointed to by the given foreign-key.
1795 + Sets the QDjangoModel pointed to by the given foreign-key.
1801 - \note The \c model will take ownership of the given \c value.
1802 + \note The \c model will take ownership of the given \c value.
1804 void QDjangoMetaModel::setForeignKey(QObject *model, const char *name, QObject *value) const
1806 - const QByteArray prop(name);
1807 - QObject *old = model->property(prop + "_ptr").value<QObject*>();
1811 - // store the new pointer and update the foreign key
1812 - model->setProperty(prop + "_ptr", qVariantFromValue(value));
1815 - const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[prop]);
1816 - model->setProperty(prop + "_id", value->property(foreignMeta.primaryKey()));
1818 - model->setProperty(prop + "_id", QVariant());
1820 + const QByteArray prop(name);
1821 + QObject *old = model->property(prop + "_ptr").value<QObject*>();
1825 + // store the new pointer and update the foreign key
1826 + model->setProperty(prop + "_ptr", qVariantFromValue(value));
1829 + const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[prop]);
1830 + model->setProperty(prop + "_id", value->property(foreignMeta.primaryKey()));
1832 + model->setProperty(prop + "_id", QVariant());
1836 @@ -474,23 +481,23 @@
1838 - Loads the given properties into a \a model instance.
1839 + Loads the given properties into a \a model instance.
1841 void QDjangoMetaModel::load(QObject *model, const QVariantList &properties, int &pos) const
1843 - // process local fields
1844 - foreach (const QDjangoMetaField &field, d->localFields)
1845 - model->setProperty(field.d->name, properties.at(pos++));
1847 - // process foreign fields
1848 - if (pos >= properties.size())
1850 - foreach (const QByteArray &fkName, d->foreignFields.keys())
1852 - QObject *object = model->property(fkName + "_ptr").value<QObject*>();
1855 - const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[fkName]);
1856 - foreignMeta.load(object, properties, pos);
1859 + // process local fields
1860 + foreach (const QDjangoMetaField &field, d->localFields)
1861 + model->setProperty(field.d->name, properties.at(pos++));
1863 + // process foreign fields
1864 + if (pos >= properties.size())
1866 + foreach (const QByteArray &fkName, d->foreignFields.keys())
1868 + QObject *object = model->property(fkName + "_ptr").value<QObject*>();
1871 + const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[fkName]);
1872 + foreignMeta.load(object, properties, pos);
1879 - Returns the foreign field mapping.
1880 + Returns the foreign field mapping.
1882 QMap<QByteArray, QString> QDjangoMetaModel::foreignFields() const
1884 - return d->foreignFields;
1885 + return d->foreignFields;
1888 @@ -505,13 +512,13 @@
1890 - Return the local field with the specified \a name.
1891 + Return the local field with the specified \a name.
1893 QDjangoMetaField QDjangoMetaModel::localField(const QString &name) const
1895 - const QString fieldName = (name == "pk") ? d->primaryKey : name;
1896 - foreach (const QDjangoMetaField &field, d->localFields) {
1897 - if (field.d->name == fieldName)
1900 - return QDjangoMetaField();
1901 + const QString fieldName = (name == "pk") ? d->primaryKey : name;
1902 + foreach (const QDjangoMetaField &field, d->localFields) {
1903 + if (field.d->name == fieldName)
1906 + return QDjangoMetaField();
1911 - Returns the list of local fields.
1912 + Returns the list of local fields.
1914 QList<QDjangoMetaField> QDjangoMetaModel::localFields() const
1916 - return d->localFields;
1917 + return d->localFields;
1922 - Returns the name of the primary key for the current QDjangoMetaModel.
1923 + Returns the name of the primary key for the current QDjangoMetaModel.
1925 QByteArray QDjangoMetaModel::primaryKey() const
1927 - return d->primaryKey;
1928 + return d->primaryKey;
1933 - Returns the name of the database table.
1934 + Returns the name of the database table.
1936 QString QDjangoMetaModel::table() const
1942 @@ -542,11 +549,11 @@
1944 - Removes the given \a model instance from the database.
1945 + Removes the given \a model instance from the database.
1947 bool QDjangoMetaModel::remove(QObject *model) const
1949 - const QVariant pk = model->property(d->primaryKey);
1950 - QDjangoQuerySetPrivate qs(model->metaObject()->className());
1951 - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk));
1952 - return qs.sqlDelete();
1953 + const QVariant pk = model->property(d->primaryKey);
1954 + QDjangoQuerySetPrivate qs(model->metaObject()->className());
1955 + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk));
1956 + return qs.sqlDelete();
1959 @@ -553,57 +560,57 @@
1961 - Saves the given \a model instance to the database.
1962 + Saves the given \a model instance to the database.
1964 - \return true if saving succeeded, false otherwise
1965 + \return true if saving succeeded, false otherwise
1967 bool QDjangoMetaModel::save(QObject *model) const
1969 - // find primary key
1970 - const QDjangoMetaField primaryKey = localField("pk");
1971 - const QVariant pk = model->property(d->primaryKey);
1972 - if (!pk.isNull() && !(primaryKey.d->type == QVariant::Int && !pk.toInt()))
1974 - QSqlDatabase db = QDjango::database();
1975 - QDjangoQuery query(db);
1976 - query.prepare(QString("SELECT 1 AS a FROM %1 WHERE %2 = ?").arg(
1977 - db.driver()->escapeIdentifier(d->table, QSqlDriver::FieldName),
1978 - db.driver()->escapeIdentifier(primaryKey.column(), QSqlDriver::FieldName)));
1979 - query.addBindValue(pk);
1980 - if (query.exec() && query.next())
1983 - QVariantMap fields;
1984 - foreach (const QDjangoMetaField &field, d->localFields) {
1985 - if (field.d->name != d->primaryKey) {
1986 - const QVariant value = model->property(field.d->name);
1987 - fields.insert(field.d->name, field.toDatabase(value));
1992 - QDjangoQuerySetPrivate qs(model->metaObject()->className());
1993 - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk));
1994 - return qs.sqlUpdate(fields) != -1;
1999 - QVariantMap fields;
2000 - foreach (const QDjangoMetaField &field, d->localFields) {
2001 - if (!field.d->autoIncrement) {
2002 - const QVariant value = model->property(field.d->name);
2003 - fields.insert(field.name(), field.toDatabase(value));
2008 - QVariant insertId;
2009 - QDjangoQuerySetPrivate qs(model->metaObject()->className());
2010 - if (!qs.sqlInsert(fields, &insertId))
2013 - // fetch autoincrement pk
2014 - if (primaryKey.d->autoIncrement)
2015 - model->setProperty(d->primaryKey, insertId);
2017 + // find primary key
2018 + const QDjangoMetaField primaryKey = localField("pk");
2019 + const QVariant pk = model->property(d->primaryKey);
2020 + if (!pk.isNull() && !(primaryKey.d->type == QVariant::Int && !pk.toInt()))
2022 + QSqlDatabase db = QDjango::database();
2023 + QDjangoQuery query(db);
2024 + query.prepare(QString("SELECT 1 AS a FROM %1 WHERE %2 = ?").arg(
2025 + db.driver()->escapeIdentifier(d->table, QSqlDriver::FieldName),
2026 + db.driver()->escapeIdentifier(primaryKey.column(), QSqlDriver::FieldName)));
2027 + query.addBindValue(pk);
2028 + if (query.exec() && query.next())
2031 + QVariantMap fields;
2032 + foreach (const QDjangoMetaField &field, d->localFields) {
2033 + if (field.d->name != d->primaryKey) {
2034 + const QVariant value = model->property(field.d->name);
2035 + fields.insert(field.d->name, field.toDatabase(value));
2040 + QDjangoQuerySetPrivate qs(model->metaObject()->className());
2041 + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk));
2042 + return qs.sqlUpdate(fields) != -1;
2047 + QVariantMap fields;
2048 + foreach (const QDjangoMetaField &field, d->localFields) {
2049 + if (!field.d->autoIncrement) {
2050 + const QVariant value = model->property(field.d->name);
2051 + fields.insert(field.name(), field.toDatabase(value));
2056 + QVariant insertId;
2057 + QDjangoQuerySetPrivate qs(model->metaObject()->className());
2058 + if (!qs.sqlInsert(fields, &insertId))
2061 + // fetch autoincrement pk
2062 + if (primaryKey.d->autoIncrement)
2063 + model->setProperty(d->primaryKey, insertId);
2068 ## The extracted whitespace changes
2071 --- a/qdjango-0.2.6/src/db/QDjangoMetaModel.cpp
2072 +++ b/qdjango-0.2.6/src/db/QDjangoMetaModel.cpp
2073 @@ -231,6 +231,13 @@ QDjangoMetaModel::QDjangoMetaModel(const QObject *model)
2074 field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption;
2075 field.d->index = true;
2076 field.d->null = nullOption;
2077 + if (primaryKeyOption) {
2078 + field.d->autoIncrement = autoIncrementOption;
2079 + field.d->unique = true;
2080 + d->primaryKey = field.d->name;
2081 + } else if (uniqueOption) {
2082 + field.d->unique = true;
2084 d->localFields << field;