[[!meta title="Edit stages"]] [[!meta author="rohieb"]] [[!meta license="GNU LGPL, v2.1 or later"]] [[!toc]] This page shows the different edit stages of a 1000-line patch which mostly consisted of white space changes in one single hunk, and which was subsequently split into smaller hunks. See the [[related blog post|../splitting-overly-large-hunks-in-patches]] for an explanation what was done. The code below originates from the [QDjango][] project, which is released under the terms of the [GNU Lesser General Public License][gnu-lgpl], version 2.1 or later. [QDjango]: https://code.google.com/p/qdjango/ [gnu-lgpl]: http://www.gnu.org/licenses/lgpl.html ## The original patch …with far too much whitespace changes: [[!format diff """ --- qdjango-0.2.6.orig/src/db/QDjangoMetaModel.cpp 2012-09-09 07:34:12.000000000 +0200 +++ qdjango-0.2.6/src/db/QDjangoMetaModel.cpp 2012-09-09 08:35:54.000000000 +0200 @@ -27,583 +27,590 @@ class QDjangoMetaFieldPrivate : public QSharedData { public: - QDjangoMetaFieldPrivate(); + QDjangoMetaFieldPrivate(); - bool autoIncrement; - QString db_column; - QString foreignModel; - bool index; - int maxLength; - QByteArray name; - bool null; - QVariant::Type type; - bool unique; + bool autoIncrement; + QString db_column; + QString foreignModel; + bool index; + int maxLength; + QByteArray name; + bool null; + QVariant::Type type; + bool unique; }; QDjangoMetaFieldPrivate::QDjangoMetaFieldPrivate() - : autoIncrement(false), - index(false), - maxLength(0), - null(false), - unique(false) + : autoIncrement(false), + index(false), + maxLength(0), + null(false), + unique(false) { } /*! - Constructs a new QDjangoMetaField. + Constructs a new QDjangoMetaField. */ QDjangoMetaField::QDjangoMetaField() { - d = new QDjangoMetaFieldPrivate; + d = new QDjangoMetaFieldPrivate; } /*! - Constructs a copy of \a other. + Constructs a copy of \a other. */ QDjangoMetaField::QDjangoMetaField(const QDjangoMetaField &other) - : d(other.d) + : d(other.d) { } /*! - Destroys the meta field. + Destroys the meta field. */ QDjangoMetaField::~QDjangoMetaField() { } /*! - Assigns \a other to this meta field. + Assigns \a other to this meta field. */ QDjangoMetaField& QDjangoMetaField::operator=(const QDjangoMetaField& other) { - d = other.d; - return *this; + d = other.d; + return *this; } /*! - Returns the database column for this meta field. + Returns the database column for this meta field. */ QString QDjangoMetaField::column() const { - return d->db_column; + return d->db_column; } /*! - Returns true if this is a valid field. + Returns true if this is a valid field. */ bool QDjangoMetaField::isValid() const { - return !d->name.isEmpty(); + return !d->name.isEmpty(); } /*! - Returns name of this meta field. + Returns name of this meta field. */ QString QDjangoMetaField::name() const { - return QString::fromLatin1(d->name); + return QString::fromLatin1(d->name); } /*! - Transforms the given field value for database storage. + Transforms the given field value for database storage. */ QVariant QDjangoMetaField::toDatabase(const QVariant &value) const { - if (d->type == QVariant::String && !d->null && value.isNull()) - return QString(""); - else if (!d->foreignModel.isEmpty() && d->type == QVariant::Int && d->null && !value.toInt()) { - // store 0 foreign key as NULL if the field is NULL - return QVariant(); - } else - return value; + if (d->type == QVariant::String && !d->null && value.isNull()) + return QString(""); + else if (!d->foreignModel.isEmpty() && d->type == QVariant::Int && d->null && !value.toInt()) { + // store 0 foreign key as NULL if the field is NULL + return QVariant(); + } else + return value; } static QMap parseOptions(const char *value) { - QMap options; - QStringList items = QString::fromUtf8(value).split(' '); - foreach (const QString &item, items) { - QStringList assign = item.split('='); - if (assign.size() == 2) { - options[assign[0].toLower()] = assign[1]; - } else { - qWarning() << "Could not parse option" << item; - } - } - return options; + QMap options; + QStringList items = QString::fromUtf8(value).split(' '); + foreach (const QString &item, items) { + QStringList assign = item.split('='); + if (assign.size() == 2) { + options[assign[0].toLower()] = assign[1]; + } else { + qWarning() << "Could not parse option" << item; + } + } + return options; } class QDjangoMetaModelPrivate : public QSharedData { public: - QList localFields; - QMap foreignFields; - QByteArray primaryKey; - QString table; + QList localFields; + QMap foreignFields; + QByteArray primaryKey; + QString table; }; /*! - Constructs a new QDjangoMetaModel by inspecting the given \a model instance. + Constructs a new QDjangoMetaModel by inspecting the given \a model instance. */ QDjangoMetaModel::QDjangoMetaModel(const QObject *model) - : d(new QDjangoMetaModelPrivate) + : d(new QDjangoMetaModelPrivate) { - if (!model) - return; + if (!model) + return; - const QMetaObject* meta = model->metaObject(); - d->table = QString(meta->className()).toLower().toLatin1(); + const QMetaObject* meta = model->metaObject(); + d->table = QString(meta->className()).toLower().toLatin1(); + + // parse table options + const int optionsIndex = meta->indexOfClassInfo("__meta__"); + if (optionsIndex >= 0) { + QMap options = parseOptions(meta->classInfo(optionsIndex).value()); + QMapIterator option(options); + while (option.hasNext()) { + option.next(); + if (option.key() == "db_table") + d->table = option.value(); + } + } + + const int count = meta->propertyCount(); + for(int i = QObject::staticMetaObject.propertyCount(); i < count; ++i) + { + QString typeName = meta->property(i).typeName(); + if (!qstrcmp(meta->property(i).name(), "pk")) + continue; + + // parse field options + bool autoIncrementOption = false; + QString dbColumnOption; + bool dbIndexOption = false; + bool ignoreFieldOption = false; + int maxLengthOption = 0; + bool primaryKeyOption = false; + bool nullOption = false; + bool uniqueOption = false; + const int infoIndex = meta->indexOfClassInfo(meta->property(i).name()); + if (infoIndex >= 0) + { + QMap options = parseOptions(meta->classInfo(infoIndex).value()); + QMapIterator option(options); + while (option.hasNext()) { + option.next(); + const QString value = option.value(); + if (option.key() == "auto_increment") + autoIncrementOption = (value.toLower() == "true" || value == "1"); + else if (option.key() == "db_column") + dbColumnOption = value; + else if (option.key() == "db_index") + dbIndexOption = (value.toLower() == "true" || value == "1"); + else if (option.key() == "ignore_field") + ignoreFieldOption = (value.toLower() == "true" || value == "1"); + else if (option.key() == "max_length") + maxLengthOption = value.toInt(); + else if (option.key() == "null") + nullOption = (value.toLower() == "true" || value == "1"); + else if (option.key() == "primary_key") + primaryKeyOption = (value.toLower() == "true" || value == "1"); + else if (option.key() == "unique") + uniqueOption = (value.toLower() == "true" || value == "1"); + } + } + + // ignore field + if (ignoreFieldOption) + continue; + + // foreign field + if (typeName.endsWith("*")) + { + const QByteArray fkName = meta->property(i).name(); + const QString fkModel = typeName.left(typeName.size() - 1); + d->foreignFields.insert(fkName, fkModel); + + QDjangoMetaField field; + field.d->name = fkName + "_id"; + // FIXME : the key is not necessarily an INTEGER field, we should + // probably perform a lookup on the foreign model, but are we sure + // it is already registered? + field.d->type = QVariant::Int; + field.d->foreignModel = fkModel; + field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption; + field.d->index = true; + field.d->null = nullOption; + if (primaryKeyOption) { + field.d->autoIncrement = autoIncrementOption; + field.d->unique = true; + d->primaryKey = field.d->name; + } else if (uniqueOption) { + field.d->unique = true; + } + d->localFields << field; + continue; + } + + // local field + QDjangoMetaField field; + field.d->name = meta->property(i).name(); + field.d->type = meta->property(i).type(); + field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption; + field.d->index = dbIndexOption; + field.d->maxLength = maxLengthOption; + field.d->null = nullOption; + if (primaryKeyOption) { + field.d->autoIncrement = autoIncrementOption; + field.d->unique = true; + d->primaryKey = field.d->name; + } else if (uniqueOption) { + field.d->unique = true; + } + + d->localFields << field; + } + + // automatic primary key + if (d->primaryKey.isEmpty()) { + QDjangoMetaField field; + field.d->name = "id"; + field.d->type = QVariant::Int; + field.d->db_column = "id"; + field.d->autoIncrement = true; + field.d->unique = true; + d->localFields.prepend(field); + d->primaryKey = field.d->name; + } - // parse table options - const int optionsIndex = meta->indexOfClassInfo("__meta__"); - if (optionsIndex >= 0) { - QMap options = parseOptions(meta->classInfo(optionsIndex).value()); - QMapIterator option(options); - while (option.hasNext()) { - option.next(); - if (option.key() == "db_table") - d->table = option.value(); - } - } - - const int count = meta->propertyCount(); - for(int i = QObject::staticMetaObject.propertyCount(); i < count; ++i) - { - QString typeName = meta->property(i).typeName(); - if (!qstrcmp(meta->property(i).name(), "pk")) - continue; - - // parse field options - bool autoIncrementOption = false; - QString dbColumnOption; - bool dbIndexOption = false; - bool ignoreFieldOption = false; - int maxLengthOption = 0; - bool primaryKeyOption = false; - bool nullOption = false; - bool uniqueOption = false; - const int infoIndex = meta->indexOfClassInfo(meta->property(i).name()); - if (infoIndex >= 0) - { - QMap options = parseOptions(meta->classInfo(infoIndex).value()); - QMapIterator option(options); - while (option.hasNext()) { - option.next(); - const QString value = option.value(); - if (option.key() == "auto_increment") - autoIncrementOption = (value.toLower() == "true" || value == "1"); - else if (option.key() == "db_column") - dbColumnOption = value; - else if (option.key() == "db_index") - dbIndexOption = (value.toLower() == "true" || value == "1"); - else if (option.key() == "ignore_field") - ignoreFieldOption = (value.toLower() == "true" || value == "1"); - else if (option.key() == "max_length") - maxLengthOption = value.toInt(); - else if (option.key() == "null") - nullOption = (value.toLower() == "true" || value == "1"); - else if (option.key() == "primary_key") - primaryKeyOption = (value.toLower() == "true" || value == "1"); - else if (option.key() == "unique") - uniqueOption = (value.toLower() == "true" || value == "1"); - } - } - - // ignore field - if (ignoreFieldOption) - continue; - - // foreign field - if (typeName.endsWith("*")) - { - const QByteArray fkName = meta->property(i).name(); - const QString fkModel = typeName.left(typeName.size() - 1); - d->foreignFields.insert(fkName, fkModel); - - QDjangoMetaField field; - field.d->name = fkName + "_id"; - // FIXME : the key is not necessarily an INTEGER field, we should - // probably perform a lookup on the foreign model, but are we sure - // it is already registered? - field.d->type = QVariant::Int; - field.d->foreignModel = fkModel; - field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption; - field.d->index = true; - field.d->null = nullOption; - d->localFields << field; - continue; - } - - // local field - QDjangoMetaField field; - field.d->name = meta->property(i).name(); - field.d->type = meta->property(i).type(); - field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption; - field.d->index = dbIndexOption; - field.d->maxLength = maxLengthOption; - field.d->null = nullOption; - if (primaryKeyOption) { - field.d->autoIncrement = autoIncrementOption; - field.d->unique = true; - d->primaryKey = field.d->name; - } else if (uniqueOption) { - field.d->unique = true; - } - - d->localFields << field; - } - - // automatic primary key - if (d->primaryKey.isEmpty()) { - QDjangoMetaField field; - field.d->name = "id"; - field.d->type = QVariant::Int; - field.d->db_column = "id"; - field.d->autoIncrement = true; - field.d->unique = true; - d->localFields.prepend(field); - d->primaryKey = field.d->name; - } - } /*! - Constructs a copy of \a other. + Constructs a copy of \a other. */ QDjangoMetaModel::QDjangoMetaModel(const QDjangoMetaModel &other) - : d(other.d) + : d(other.d) { } /*! - Destroys the meta model. + Destroys the meta model. */ QDjangoMetaModel::~QDjangoMetaModel() { } /*! - Assigns \a other to this meta model. + Assigns \a other to this meta model. */ QDjangoMetaModel& QDjangoMetaModel::operator=(const QDjangoMetaModel& other) { - d = other.d; - return *this; + d = other.d; + return *this; } /*! - Creates the database table for this QDjangoMetaModel. + Creates the database table for this QDjangoMetaModel. */ bool QDjangoMetaModel::createTable() const { - QSqlDatabase db = QDjango::database(); - QSqlDriver *driver = db.driver(); - const QString driverName = db.driverName(); - - QStringList propSql; - const QString quotedTable = db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName); - foreach (const QDjangoMetaField &field, d->localFields) - { - QString fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName); - switch (field.d->type) { - case QVariant::Bool: - fieldSql += " BOOLEAN"; - break; - case QVariant::ByteArray: - if (driverName == QLatin1String("QPSQL")) - fieldSql += " BYTEA"; - else { - fieldSql += " BLOB"; - if (field.d->maxLength > 0) - fieldSql += QString("(%1)").arg(field.d->maxLength); - } - break; - case QVariant::Date: - fieldSql += " DATE"; - break; - case QVariant::DateTime: - if (driverName == QLatin1String("QPSQL")) - fieldSql += " TIMESTAMP"; - else - fieldSql += " DATETIME"; - break; - case QVariant::Double: - fieldSql += " REAL"; - break; - case QVariant::Int: - fieldSql += " INTEGER"; - break; - case QVariant::LongLong: - fieldSql += " BIGINT"; - break; - case QVariant::String: - if (field.d->maxLength > 0) - fieldSql += QString(" VARCHAR(%1)").arg(field.d->maxLength); - else - fieldSql += " TEXT"; - break; - case QVariant::Time: - fieldSql += " TIME"; - break; - default: - qWarning() << "Unhandled type" << field.d->type << "for property" << field.d->name; - continue; - } - - if (!field.d->null) - fieldSql += " NOT NULL"; - - // primary key - if (field.d->name == d->primaryKey) - fieldSql += " PRIMARY KEY"; - - // auto-increment is backend specific - if (field.d->autoIncrement) { - if (driverName == QLatin1String("QSQLITE") || - driverName == QLatin1String("QSQLITE2")) - fieldSql += QLatin1String(" AUTOINCREMENT"); - else if (driverName == QLatin1String("QMYSQL")) - fieldSql += QLatin1String(" AUTO_INCREMENT"); - else if (driverName == QLatin1String("QPSQL")) - fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName) + " SERIAL PRIMARY KEY"; - } - - // foreign key - if (!field.d->foreignModel.isEmpty()) - { - const QDjangoMetaModel foreignMeta = QDjango::metaModel(field.d->foreignModel); - const QDjangoMetaField foreignField = foreignMeta.localField("pk"); - fieldSql += QString(" REFERENCES %1 (%2)").arg( - driver->escapeIdentifier(foreignMeta.d->table, QSqlDriver::TableName), - driver->escapeIdentifier(foreignField.column(), QSqlDriver::FieldName)); - } - propSql << fieldSql; - } - - // create table - QDjangoQuery createQuery(db); - if (!createQuery.exec(QString("CREATE TABLE %1 (%2)").arg( - quotedTable, - propSql.join(", ")))) - return false; - - // create indices - foreach (const QDjangoMetaField &field, d->localFields) { - if (field.d->index && !field.d->unique) { - const QString indexName = d->table + "_" + field.column(); - if (!createQuery.exec(QString("CREATE INDEX %1 ON %2 (%3)").arg( - // FIXME : how should we escape an index name? - driver->escapeIdentifier(indexName, QSqlDriver::FieldName), - quotedTable, - driver->escapeIdentifier(field.column(), QSqlDriver::FieldName)))) - return false; - } - } + QSqlDatabase db = QDjango::database(); + QSqlDriver *driver = db.driver(); + const QString driverName = db.driverName(); + + QStringList propSql; + const QString quotedTable = db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName); + foreach (const QDjangoMetaField &field, d->localFields) + { + QString fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName); + switch (field.d->type) { + case QVariant::Bool: + fieldSql += " BOOLEAN"; + break; + case QVariant::ByteArray: + if (driverName == QLatin1String("QPSQL")) + fieldSql += " BYTEA"; + else { + fieldSql += " BLOB"; + if (field.d->maxLength > 0) + fieldSql += QString("(%1)").arg(field.d->maxLength); + } + break; + case QVariant::Date: + fieldSql += " DATE"; + break; + case QVariant::DateTime: + if (driverName == QLatin1String("QPSQL")) + fieldSql += " TIMESTAMP"; + else + fieldSql += " DATETIME"; + break; + case QVariant::Double: + fieldSql += " REAL"; + break; + case QVariant::Int: + fieldSql += " INTEGER"; + break; + case QVariant::LongLong: + fieldSql += " BIGINT"; + break; + case QVariant::String: + if (field.d->maxLength > 0) + fieldSql += QString(" VARCHAR(%1)").arg(field.d->maxLength); + else + fieldSql += " TEXT"; + break; + case QVariant::Time: + fieldSql += " TIME"; + break; + default: + qWarning() << "Unhandled type" << field.d->type << "for property" << field.d->name; + continue; + } + + if (!field.d->null) + fieldSql += " NOT NULL"; + + // primary key + if (field.d->name == d->primaryKey) + fieldSql += " PRIMARY KEY"; + + // auto-increment is backend specific + if (field.d->autoIncrement) { + if (driverName == QLatin1String("QSQLITE") || + driverName == QLatin1String("QSQLITE2")) + fieldSql += QLatin1String(" AUTOINCREMENT"); + else if (driverName == QLatin1String("QMYSQL")) + fieldSql += QLatin1String(" AUTO_INCREMENT"); + else if (driverName == QLatin1String("QPSQL")) + fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName) + " SERIAL PRIMARY KEY"; + } + + // foreign key + if (!field.d->foreignModel.isEmpty()) + { + const QDjangoMetaModel foreignMeta = QDjango::metaModel(field.d->foreignModel); + const QDjangoMetaField foreignField = foreignMeta.localField("pk"); + fieldSql += QString(" REFERENCES %1 (%2)").arg( + driver->escapeIdentifier(foreignMeta.d->table, QSqlDriver::TableName), + driver->escapeIdentifier(foreignField.column(), QSqlDriver::FieldName)); + } + propSql << fieldSql; + } + + // create table + QDjangoQuery createQuery(db); + if (!createQuery.exec(QString("CREATE TABLE %1 (%2)").arg( + quotedTable, + propSql.join(", ")))) + return false; + + // create indices + foreach (const QDjangoMetaField &field, d->localFields) { + if (field.d->index && !field.d->unique) { + const QString indexName = d->table + "_" + field.column(); + if (!createQuery.exec(QString("CREATE INDEX %1 ON %2 (%3)").arg( + // FIXME : how should we escape an index name? + driver->escapeIdentifier(indexName, QSqlDriver::FieldName), + quotedTable, + driver->escapeIdentifier(field.column(), QSqlDriver::FieldName)))) + return false; + } + } - return true; + return true; } /*! - Drops the database table for this QDjangoMetaModel. + Drops the database table for this QDjangoMetaModel. */ bool QDjangoMetaModel::dropTable() const { - QSqlDatabase db = QDjango::database(); + QSqlDatabase db = QDjango::database(); - QDjangoQuery query(db); - return query.exec(QString("DROP TABLE %1").arg( - db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName))); + QDjangoQuery query(db); + return query.exec(QString("DROP TABLE %1").arg( + db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName))); } /*! - Retrieves the QDjangoModel pointed to by the given foreign-key. + Retrieves the QDjangoModel pointed to by the given foreign-key. - \param model - \param name + \param model + \param name */ QObject *QDjangoMetaModel::foreignKey(const QObject *model, const char *name) const { - const QByteArray prop(name); - QObject *foreign = model->property(prop + "_ptr").value(); - if (!foreign) - return 0; - - // if the foreign object was not loaded yet, do it now - const QString foreignClass = d->foreignFields[prop]; - const QDjangoMetaModel foreignMeta = QDjango::metaModel(foreignClass); - const QVariant foreignPk = model->property(prop + "_id"); - if (foreign->property(foreignMeta.primaryKey()) != foreignPk) - { - QDjangoQuerySetPrivate qs(foreignClass); - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, foreignPk)); - qs.sqlFetch(); - if (qs.properties.size() != 1 || !qs.sqlLoad(foreign, 0)) - return 0; - } - return foreign; + const QByteArray prop(name); + QObject *foreign = model->property(prop + "_ptr").value(); + if (!foreign) + return 0; + + // if the foreign object was not loaded yet, do it now + const QString foreignClass = d->foreignFields[prop]; + const QDjangoMetaModel foreignMeta = QDjango::metaModel(foreignClass); + const QVariant foreignPk = model->property(prop + "_id"); + if (foreign->property(foreignMeta.primaryKey()) != foreignPk) + { + QDjangoQuerySetPrivate qs(foreignClass); + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, foreignPk)); + qs.sqlFetch(); + if (qs.properties.size() != 1 || !qs.sqlLoad(foreign, 0)) + return 0; + } + return foreign; } /*! - Sets the QDjangoModel pointed to by the given foreign-key. - - \param model - \param name - \param value + Sets the QDjangoModel pointed to by the given foreign-key. + + \param model + \param name + \param value - \note The \c model will take ownership of the given \c value. + \note The \c model will take ownership of the given \c value. */ void QDjangoMetaModel::setForeignKey(QObject *model, const char *name, QObject *value) const { - const QByteArray prop(name); - QObject *old = model->property(prop + "_ptr").value(); - if (old == value) - return; - - // store the new pointer and update the foreign key - model->setProperty(prop + "_ptr", qVariantFromValue(value)); - if (value) - { - const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[prop]); - model->setProperty(prop + "_id", value->property(foreignMeta.primaryKey())); - } else { - model->setProperty(prop + "_id", QVariant()); - } + const QByteArray prop(name); + QObject *old = model->property(prop + "_ptr").value(); + if (old == value) + return; + + // store the new pointer and update the foreign key + model->setProperty(prop + "_ptr", qVariantFromValue(value)); + if (value) + { + const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[prop]); + model->setProperty(prop + "_id", value->property(foreignMeta.primaryKey())); + } else { + model->setProperty(prop + "_id", QVariant()); + } } /*! - Loads the given properties into a \a model instance. + Loads the given properties into a \a model instance. */ void QDjangoMetaModel::load(QObject *model, const QVariantList &properties, int &pos) const { - // process local fields - foreach (const QDjangoMetaField &field, d->localFields) - model->setProperty(field.d->name, properties.at(pos++)); - - // process foreign fields - if (pos >= properties.size()) - return; - foreach (const QByteArray &fkName, d->foreignFields.keys()) - { - QObject *object = model->property(fkName + "_ptr").value(); - if (object) - { - const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[fkName]); - foreignMeta.load(object, properties, pos); - } - } + // process local fields + foreach (const QDjangoMetaField &field, d->localFields) + model->setProperty(field.d->name, properties.at(pos++)); + + // process foreign fields + if (pos >= properties.size()) + return; + foreach (const QByteArray &fkName, d->foreignFields.keys()) + { + QObject *object = model->property(fkName + "_ptr").value(); + if (object) + { + const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[fkName]); + foreignMeta.load(object, properties, pos); + } + } } /*! - Returns the foreign field mapping. + Returns the foreign field mapping. */ QMap QDjangoMetaModel::foreignFields() const { - return d->foreignFields; + return d->foreignFields; } /*! - Return the local field with the specified \a name. + Return the local field with the specified \a name. */ QDjangoMetaField QDjangoMetaModel::localField(const QString &name) const { - const QString fieldName = (name == "pk") ? d->primaryKey : name; - foreach (const QDjangoMetaField &field, d->localFields) { - if (field.d->name == fieldName) - return field; - } - return QDjangoMetaField(); + const QString fieldName = (name == "pk") ? d->primaryKey : name; + foreach (const QDjangoMetaField &field, d->localFields) { + if (field.d->name == fieldName) + return field; + } + return QDjangoMetaField(); } /*! - Returns the list of local fields. + Returns the list of local fields. */ QList QDjangoMetaModel::localFields() const { - return d->localFields; + return d->localFields; } /*! - Returns the name of the primary key for the current QDjangoMetaModel. + Returns the name of the primary key for the current QDjangoMetaModel. */ QByteArray QDjangoMetaModel::primaryKey() const { - return d->primaryKey; + return d->primaryKey; } /*! - Returns the name of the database table. + Returns the name of the database table. */ QString QDjangoMetaModel::table() const { - return d->table; + return d->table; } /*! - Removes the given \a model instance from the database. + Removes the given \a model instance from the database. */ bool QDjangoMetaModel::remove(QObject *model) const { - const QVariant pk = model->property(d->primaryKey); - QDjangoQuerySetPrivate qs(model->metaObject()->className()); - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk)); - return qs.sqlDelete(); + const QVariant pk = model->property(d->primaryKey); + QDjangoQuerySetPrivate qs(model->metaObject()->className()); + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk)); + return qs.sqlDelete(); } /*! - Saves the given \a model instance to the database. + Saves the given \a model instance to the database. - \return true if saving succeeded, false otherwise + \return true if saving succeeded, false otherwise */ bool QDjangoMetaModel::save(QObject *model) const { - // find primary key - const QDjangoMetaField primaryKey = localField("pk"); - const QVariant pk = model->property(d->primaryKey); - if (!pk.isNull() && !(primaryKey.d->type == QVariant::Int && !pk.toInt())) - { - QSqlDatabase db = QDjango::database(); - QDjangoQuery query(db); - query.prepare(QString("SELECT 1 AS a FROM %1 WHERE %2 = ?").arg( - db.driver()->escapeIdentifier(d->table, QSqlDriver::FieldName), - db.driver()->escapeIdentifier(primaryKey.column(), QSqlDriver::FieldName))); - query.addBindValue(pk); - if (query.exec() && query.next()) - { - // prepare data - QVariantMap fields; - foreach (const QDjangoMetaField &field, d->localFields) { - if (field.d->name != d->primaryKey) { - const QVariant value = model->property(field.d->name); - fields.insert(field.d->name, field.toDatabase(value)); - } - } - - // perform UPDATE - QDjangoQuerySetPrivate qs(model->metaObject()->className()); - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk)); - return qs.sqlUpdate(fields) != -1; - } - } - - // prepare data - QVariantMap fields; - foreach (const QDjangoMetaField &field, d->localFields) { - if (!field.d->autoIncrement) { - const QVariant value = model->property(field.d->name); - fields.insert(field.name(), field.toDatabase(value)); - } - } - - // perform INSERT - QVariant insertId; - QDjangoQuerySetPrivate qs(model->metaObject()->className()); - if (!qs.sqlInsert(fields, &insertId)) - return false; - - // fetch autoincrement pk - if (primaryKey.d->autoIncrement) - model->setProperty(d->primaryKey, insertId); - return true; + // find primary key + const QDjangoMetaField primaryKey = localField("pk"); + const QVariant pk = model->property(d->primaryKey); + if (!pk.isNull() && !(primaryKey.d->type == QVariant::Int && !pk.toInt())) + { + QSqlDatabase db = QDjango::database(); + QDjangoQuery query(db); + query.prepare(QString("SELECT 1 AS a FROM %1 WHERE %2 = ?").arg( + db.driver()->escapeIdentifier(d->table, QSqlDriver::FieldName), + db.driver()->escapeIdentifier(primaryKey.column(), QSqlDriver::FieldName))); + query.addBindValue(pk); + if (query.exec() && query.next()) + { + // prepare data + QVariantMap fields; + foreach (const QDjangoMetaField &field, d->localFields) { + if (field.d->name != d->primaryKey) { + const QVariant value = model->property(field.d->name); + fields.insert(field.d->name, field.toDatabase(value)); + } + } + + // perform UPDATE + QDjangoQuerySetPrivate qs(model->metaObject()->className()); + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk)); + return qs.sqlUpdate(fields) != -1; + } + } + + // prepare data + QVariantMap fields; + foreach (const QDjangoMetaField &field, d->localFields) { + if (!field.d->autoIncrement) { + const QVariant value = model->property(field.d->name); + fields.insert(field.name(), field.toDatabase(value)); + } + } + + // perform INSERT + QVariant insertId; + QDjangoQuerySetPrivate qs(model->metaObject()->className()); + if (!qs.sqlInsert(fields, &insertId)) + return false; + + // fetch autoincrement pk + if (primaryKey.d->autoIncrement) + model->setProperty(d->primaryKey, insertId); + return true; } """]] ## After editing with Emacs Note the additional `@@ ... @@` lines: [[!format diff """ --- qdjango-0.2.6.orig/src/db/QDjangoMetaModel.cpp 2012-09-09 07:34:12.000000000 +0200 +++ qdjango-0.2.6/src/db/QDjangoMetaModel.cpp 2012-09-09 08:35:54.000000000 +0200 @@ -27,15 +27,15 @@ class QDjangoMetaFieldPrivate : public QSharedData { public: - QDjangoMetaFieldPrivate(); + QDjangoMetaFieldPrivate(); - bool autoIncrement; - QString db_column; - QString foreignModel; - bool index; - int maxLength; - QByteArray name; - bool null; - QVariant::Type type; - bool unique; + bool autoIncrement; + QString db_column; + QString foreignModel; + bool index; + int maxLength; + QByteArray name; + bool null; + QVariant::Type type; + bool unique; }; @@ -42,9 +42,9 @@ QDjangoMetaFieldPrivate::QDjangoMetaFieldPrivate() - : autoIncrement(false), - index(false), - maxLength(0), - null(false), - unique(false) + : autoIncrement(false), + index(false), + maxLength(0), + null(false), + unique(false) { } @@ -51,8 +51,8 @@ /*! - Constructs a new QDjangoMetaField. + Constructs a new QDjangoMetaField. */ QDjangoMetaField::QDjangoMetaField() { - d = new QDjangoMetaFieldPrivate; + d = new QDjangoMetaFieldPrivate; } @@ -59,8 +59,8 @@ /*! - Constructs a copy of \a other. + Constructs a copy of \a other. */ QDjangoMetaField::QDjangoMetaField(const QDjangoMetaField &other) - : d(other.d) + : d(other.d) { } @@ -67,7 +67,7 @@ /*! - Destroys the meta field. + Destroys the meta field. */ QDjangoMetaField::~QDjangoMetaField() { } @@ -74,9 +74,9 @@ /*! - Assigns \a other to this meta field. + Assigns \a other to this meta field. */ QDjangoMetaField& QDjangoMetaField::operator=(const QDjangoMetaField& other) { - d = other.d; - return *this; + d = other.d; + return *this; } @@ -83,8 +83,8 @@ /*! - Returns the database column for this meta field. + Returns the database column for this meta field. */ QString QDjangoMetaField::column() const { - return d->db_column; + return d->db_column; } @@ -91,8 +91,8 @@ /*! - Returns true if this is a valid field. + Returns true if this is a valid field. */ bool QDjangoMetaField::isValid() const { - return !d->name.isEmpty(); + return !d->name.isEmpty(); } @@ -99,8 +99,8 @@ /*! - Returns name of this meta field. + Returns name of this meta field. */ QString QDjangoMetaField::name() const { - return QString::fromLatin1(d->name); + return QString::fromLatin1(d->name); } @@ -107,14 +107,14 @@ /*! - Transforms the given field value for database storage. + Transforms the given field value for database storage. */ QVariant QDjangoMetaField::toDatabase(const QVariant &value) const { - if (d->type == QVariant::String && !d->null && value.isNull()) - return QString(""); - else if (!d->foreignModel.isEmpty() && d->type == QVariant::Int && d->null && !value.toInt()) { - // store 0 foreign key as NULL if the field is NULL - return QVariant(); - } else - return value; + if (d->type == QVariant::String && !d->null && value.isNull()) + return QString(""); + else if (!d->foreignModel.isEmpty() && d->type == QVariant::Int && d->null && !value.toInt()) { + // store 0 foreign key as NULL if the field is NULL + return QVariant(); + } else + return value; } @@ -121,15 +121,15 @@ static QMap parseOptions(const char *value) { - QMap options; - QStringList items = QString::fromUtf8(value).split(' '); - foreach (const QString &item, items) { - QStringList assign = item.split('='); - if (assign.size() == 2) { - options[assign[0].toLower()] = assign[1]; - } else { - qWarning() << "Could not parse option" << item; - } - } - return options; + QMap options; + QStringList items = QString::fromUtf8(value).split(' '); + foreach (const QString &item, items) { + QStringList assign = item.split('='); + if (assign.size() == 2) { + options[assign[0].toLower()] = assign[1]; + } else { + qWarning() << "Could not parse option" << item; + } + } + return options; } @@ -136,12 +136,12 @@ class QDjangoMetaModelPrivate : public QSharedData { public: - QList localFields; - QMap foreignFields; - QByteArray primaryKey; - QString table; + QList localFields; + QMap foreignFields; + QByteArray primaryKey; + QString table; }; /*! - Constructs a new QDjangoMetaModel by inspecting the given \a model instance. + Constructs a new QDjangoMetaModel by inspecting the given \a model instance. @@ -148,141 +148,148 @@ */ QDjangoMetaModel::QDjangoMetaModel(const QObject *model) - : d(new QDjangoMetaModelPrivate) + : d(new QDjangoMetaModelPrivate) { - if (!model) - return; + if (!model) + return; - const QMetaObject* meta = model->metaObject(); - d->table = QString(meta->className()).toLower().toLatin1(); + const QMetaObject* meta = model->metaObject(); + d->table = QString(meta->className()).toLower().toLatin1(); + + // parse table options + const int optionsIndex = meta->indexOfClassInfo("__meta__"); + if (optionsIndex >= 0) { + QMap options = parseOptions(meta->classInfo(optionsIndex).value()); + QMapIterator option(options); + while (option.hasNext()) { + option.next(); + if (option.key() == "db_table") + d->table = option.value(); + } + } + + const int count = meta->propertyCount(); + for(int i = QObject::staticMetaObject.propertyCount(); i < count; ++i) + { + QString typeName = meta->property(i).typeName(); + if (!qstrcmp(meta->property(i).name(), "pk")) + continue; + + // parse field options + bool autoIncrementOption = false; + QString dbColumnOption; + bool dbIndexOption = false; + bool ignoreFieldOption = false; + int maxLengthOption = 0; + bool primaryKeyOption = false; + bool nullOption = false; + bool uniqueOption = false; + const int infoIndex = meta->indexOfClassInfo(meta->property(i).name()); + if (infoIndex >= 0) + { + QMap options = parseOptions(meta->classInfo(infoIndex).value()); + QMapIterator option(options); + while (option.hasNext()) { + option.next(); + const QString value = option.value(); + if (option.key() == "auto_increment") + autoIncrementOption = (value.toLower() == "true" || value == "1"); + else if (option.key() == "db_column") + dbColumnOption = value; + else if (option.key() == "db_index") + dbIndexOption = (value.toLower() == "true" || value == "1"); + else if (option.key() == "ignore_field") + ignoreFieldOption = (value.toLower() == "true" || value == "1"); + else if (option.key() == "max_length") + maxLengthOption = value.toInt(); + else if (option.key() == "null") + nullOption = (value.toLower() == "true" || value == "1"); + else if (option.key() == "primary_key") + primaryKeyOption = (value.toLower() == "true" || value == "1"); + else if (option.key() == "unique") + uniqueOption = (value.toLower() == "true" || value == "1"); + } + } + + // ignore field + if (ignoreFieldOption) + continue; + + // foreign field + if (typeName.endsWith("*")) + { + const QByteArray fkName = meta->property(i).name(); + const QString fkModel = typeName.left(typeName.size() - 1); + d->foreignFields.insert(fkName, fkModel); + + QDjangoMetaField field; + field.d->name = fkName + "_id"; + // FIXME : the key is not necessarily an INTEGER field, we should + // probably perform a lookup on the foreign model, but are we sure + // it is already registered? + field.d->type = QVariant::Int; + field.d->foreignModel = fkModel; + field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption; + field.d->index = true; + field.d->null = nullOption; + if (primaryKeyOption) { + field.d->autoIncrement = autoIncrementOption; + field.d->unique = true; + d->primaryKey = field.d->name; + } else if (uniqueOption) { + field.d->unique = true; + } + d->localFields << field; + continue; + } + + // local field + QDjangoMetaField field; + field.d->name = meta->property(i).name(); + field.d->type = meta->property(i).type(); + field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption; + field.d->index = dbIndexOption; + field.d->maxLength = maxLengthOption; + field.d->null = nullOption; + if (primaryKeyOption) { + field.d->autoIncrement = autoIncrementOption; + field.d->unique = true; + d->primaryKey = field.d->name; + } else if (uniqueOption) { + field.d->unique = true; + } + + d->localFields << field; + } + + // automatic primary key + if (d->primaryKey.isEmpty()) { + QDjangoMetaField field; + field.d->name = "id"; + field.d->type = QVariant::Int; + field.d->db_column = "id"; + field.d->autoIncrement = true; + field.d->unique = true; + d->localFields.prepend(field); + d->primaryKey = field.d->name; + } - // parse table options - const int optionsIndex = meta->indexOfClassInfo("__meta__"); - if (optionsIndex >= 0) { - QMap options = parseOptions(meta->classInfo(optionsIndex).value()); - QMapIterator option(options); - while (option.hasNext()) { - option.next(); - if (option.key() == "db_table") - d->table = option.value(); - } - } - - const int count = meta->propertyCount(); - for(int i = QObject::staticMetaObject.propertyCount(); i < count; ++i) - { - QString typeName = meta->property(i).typeName(); - if (!qstrcmp(meta->property(i).name(), "pk")) - continue; - - // parse field options - bool autoIncrementOption = false; - QString dbColumnOption; - bool dbIndexOption = false; - bool ignoreFieldOption = false; - int maxLengthOption = 0; - bool primaryKeyOption = false; - bool nullOption = false; - bool uniqueOption = false; - const int infoIndex = meta->indexOfClassInfo(meta->property(i).name()); - if (infoIndex >= 0) - { - QMap options = parseOptions(meta->classInfo(infoIndex).value()); - QMapIterator option(options); - while (option.hasNext()) { - option.next(); - const QString value = option.value(); - if (option.key() == "auto_increment") - autoIncrementOption = (value.toLower() == "true" || value == "1"); - else if (option.key() == "db_column") - dbColumnOption = value; - else if (option.key() == "db_index") - dbIndexOption = (value.toLower() == "true" || value == "1"); - else if (option.key() == "ignore_field") - ignoreFieldOption = (value.toLower() == "true" || value == "1"); - else if (option.key() == "max_length") - maxLengthOption = value.toInt(); - else if (option.key() == "null") - nullOption = (value.toLower() == "true" || value == "1"); - else if (option.key() == "primary_key") - primaryKeyOption = (value.toLower() == "true" || value == "1"); - else if (option.key() == "unique") - uniqueOption = (value.toLower() == "true" || value == "1"); - } - } - - // ignore field - if (ignoreFieldOption) - continue; - - // foreign field - if (typeName.endsWith("*")) - { - const QByteArray fkName = meta->property(i).name(); - const QString fkModel = typeName.left(typeName.size() - 1); - d->foreignFields.insert(fkName, fkModel); - - QDjangoMetaField field; - field.d->name = fkName + "_id"; - // FIXME : the key is not necessarily an INTEGER field, we should - // probably perform a lookup on the foreign model, but are we sure - // it is already registered? - field.d->type = QVariant::Int; - field.d->foreignModel = fkModel; - field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption; - field.d->index = true; - field.d->null = nullOption; - d->localFields << field; - continue; - } - - // local field - QDjangoMetaField field; - field.d->name = meta->property(i).name(); - field.d->type = meta->property(i).type(); - field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption; - field.d->index = dbIndexOption; - field.d->maxLength = maxLengthOption; - field.d->null = nullOption; - if (primaryKeyOption) { - field.d->autoIncrement = autoIncrementOption; - field.d->unique = true; - d->primaryKey = field.d->name; - } else if (uniqueOption) { - field.d->unique = true; - } - - d->localFields << field; - } - - // automatic primary key - if (d->primaryKey.isEmpty()) { - QDjangoMetaField field; - field.d->name = "id"; - field.d->type = QVariant::Int; - field.d->db_column = "id"; - field.d->autoIncrement = true; - field.d->unique = true; - d->localFields.prepend(field); - d->primaryKey = field.d->name; - } - } /*! - Constructs a copy of \a other. + Constructs a copy of \a other. */ QDjangoMetaModel::QDjangoMetaModel(const QDjangoMetaModel &other) - : d(other.d) + : d(other.d) { } /*! - Destroys the meta model. + Destroys the meta model. */ QDjangoMetaModel::~QDjangoMetaModel() { } /*! - Assigns \a other to this meta model. + Assigns \a other to this meta model. */ @@ -289,6 +296,6 @@ QDjangoMetaModel& QDjangoMetaModel::operator=(const QDjangoMetaModel& other) { - d = other.d; - return *this; + d = other.d; + return *this; } @@ -295,112 +302,112 @@ /*! - Creates the database table for this QDjangoMetaModel. + Creates the database table for this QDjangoMetaModel. */ bool QDjangoMetaModel::createTable() const { - QSqlDatabase db = QDjango::database(); - QSqlDriver *driver = db.driver(); - const QString driverName = db.driverName(); - - QStringList propSql; - const QString quotedTable = db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName); - foreach (const QDjangoMetaField &field, d->localFields) - { - QString fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName); - switch (field.d->type) { - case QVariant::Bool: - fieldSql += " BOOLEAN"; - break; - case QVariant::ByteArray: - if (driverName == QLatin1String("QPSQL")) - fieldSql += " BYTEA"; - else { - fieldSql += " BLOB"; - if (field.d->maxLength > 0) - fieldSql += QString("(%1)").arg(field.d->maxLength); - } - break; - case QVariant::Date: - fieldSql += " DATE"; - break; - case QVariant::DateTime: - if (driverName == QLatin1String("QPSQL")) - fieldSql += " TIMESTAMP"; - else - fieldSql += " DATETIME"; - break; - case QVariant::Double: - fieldSql += " REAL"; - break; - case QVariant::Int: - fieldSql += " INTEGER"; - break; - case QVariant::LongLong: - fieldSql += " BIGINT"; - break; - case QVariant::String: - if (field.d->maxLength > 0) - fieldSql += QString(" VARCHAR(%1)").arg(field.d->maxLength); - else - fieldSql += " TEXT"; - break; - case QVariant::Time: - fieldSql += " TIME"; - break; - default: - qWarning() << "Unhandled type" << field.d->type << "for property" << field.d->name; - continue; - } - - if (!field.d->null) - fieldSql += " NOT NULL"; - - // primary key - if (field.d->name == d->primaryKey) - fieldSql += " PRIMARY KEY"; - - // auto-increment is backend specific - if (field.d->autoIncrement) { - if (driverName == QLatin1String("QSQLITE") || - driverName == QLatin1String("QSQLITE2")) - fieldSql += QLatin1String(" AUTOINCREMENT"); - else if (driverName == QLatin1String("QMYSQL")) - fieldSql += QLatin1String(" AUTO_INCREMENT"); - else if (driverName == QLatin1String("QPSQL")) - fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName) + " SERIAL PRIMARY KEY"; - } - - // foreign key - if (!field.d->foreignModel.isEmpty()) - { - const QDjangoMetaModel foreignMeta = QDjango::metaModel(field.d->foreignModel); - const QDjangoMetaField foreignField = foreignMeta.localField("pk"); - fieldSql += QString(" REFERENCES %1 (%2)").arg( - driver->escapeIdentifier(foreignMeta.d->table, QSqlDriver::TableName), - driver->escapeIdentifier(foreignField.column(), QSqlDriver::FieldName)); - } - propSql << fieldSql; - } - - // create table - QDjangoQuery createQuery(db); - if (!createQuery.exec(QString("CREATE TABLE %1 (%2)").arg( - quotedTable, - propSql.join(", ")))) - return false; - - // create indices - foreach (const QDjangoMetaField &field, d->localFields) { - if (field.d->index && !field.d->unique) { - const QString indexName = d->table + "_" + field.column(); - if (!createQuery.exec(QString("CREATE INDEX %1 ON %2 (%3)").arg( - // FIXME : how should we escape an index name? - driver->escapeIdentifier(indexName, QSqlDriver::FieldName), - quotedTable, - driver->escapeIdentifier(field.column(), QSqlDriver::FieldName)))) - return false; - } - } + QSqlDatabase db = QDjango::database(); + QSqlDriver *driver = db.driver(); + const QString driverName = db.driverName(); + + QStringList propSql; + const QString quotedTable = db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName); + foreach (const QDjangoMetaField &field, d->localFields) + { + QString fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName); + switch (field.d->type) { + case QVariant::Bool: + fieldSql += " BOOLEAN"; + break; + case QVariant::ByteArray: + if (driverName == QLatin1String("QPSQL")) + fieldSql += " BYTEA"; + else { + fieldSql += " BLOB"; + if (field.d->maxLength > 0) + fieldSql += QString("(%1)").arg(field.d->maxLength); + } + break; + case QVariant::Date: + fieldSql += " DATE"; + break; + case QVariant::DateTime: + if (driverName == QLatin1String("QPSQL")) + fieldSql += " TIMESTAMP"; + else + fieldSql += " DATETIME"; + break; + case QVariant::Double: + fieldSql += " REAL"; + break; + case QVariant::Int: + fieldSql += " INTEGER"; + break; + case QVariant::LongLong: + fieldSql += " BIGINT"; + break; + case QVariant::String: + if (field.d->maxLength > 0) + fieldSql += QString(" VARCHAR(%1)").arg(field.d->maxLength); + else + fieldSql += " TEXT"; + break; + case QVariant::Time: + fieldSql += " TIME"; + break; + default: + qWarning() << "Unhandled type" << field.d->type << "for property" << field.d->name; + continue; + } + + if (!field.d->null) + fieldSql += " NOT NULL"; + + // primary key + if (field.d->name == d->primaryKey) + fieldSql += " PRIMARY KEY"; + + // auto-increment is backend specific + if (field.d->autoIncrement) { + if (driverName == QLatin1String("QSQLITE") || + driverName == QLatin1String("QSQLITE2")) + fieldSql += QLatin1String(" AUTOINCREMENT"); + else if (driverName == QLatin1String("QMYSQL")) + fieldSql += QLatin1String(" AUTO_INCREMENT"); + else if (driverName == QLatin1String("QPSQL")) + fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName) + " SERIAL PRIMARY KEY"; + } + + // foreign key + if (!field.d->foreignModel.isEmpty()) + { + const QDjangoMetaModel foreignMeta = QDjango::metaModel(field.d->foreignModel); + const QDjangoMetaField foreignField = foreignMeta.localField("pk"); + fieldSql += QString(" REFERENCES %1 (%2)").arg( + driver->escapeIdentifier(foreignMeta.d->table, QSqlDriver::TableName), + driver->escapeIdentifier(foreignField.column(), QSqlDriver::FieldName)); + } + propSql << fieldSql; + } + + // create table + QDjangoQuery createQuery(db); + if (!createQuery.exec(QString("CREATE TABLE %1 (%2)").arg( + quotedTable, + propSql.join(", ")))) + return false; + + // create indices + foreach (const QDjangoMetaField &field, d->localFields) { + if (field.d->index && !field.d->unique) { + const QString indexName = d->table + "_" + field.column(); + if (!createQuery.exec(QString("CREATE INDEX %1 ON %2 (%3)").arg( + // FIXME : how should we escape an index name? + driver->escapeIdentifier(indexName, QSqlDriver::FieldName), + quotedTable, + driver->escapeIdentifier(field.column(), QSqlDriver::FieldName)))) + return false; + } + } - return true; + return true; } @@ -407,12 +414,12 @@ /*! - Drops the database table for this QDjangoMetaModel. + Drops the database table for this QDjangoMetaModel. */ bool QDjangoMetaModel::dropTable() const { - QSqlDatabase db = QDjango::database(); + QSqlDatabase db = QDjango::database(); - QDjangoQuery query(db); - return query.exec(QString("DROP TABLE %1").arg( - db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName))); + QDjangoQuery query(db); + return query.exec(QString("DROP TABLE %1").arg( + db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName))); } @@ -419,27 +426,27 @@ /*! - Retrieves the QDjangoModel pointed to by the given foreign-key. + Retrieves the QDjangoModel pointed to by the given foreign-key. - \param model - \param name + \param model + \param name */ QObject *QDjangoMetaModel::foreignKey(const QObject *model, const char *name) const { - const QByteArray prop(name); - QObject *foreign = model->property(prop + "_ptr").value(); - if (!foreign) - return 0; - - // if the foreign object was not loaded yet, do it now - const QString foreignClass = d->foreignFields[prop]; - const QDjangoMetaModel foreignMeta = QDjango::metaModel(foreignClass); - const QVariant foreignPk = model->property(prop + "_id"); - if (foreign->property(foreignMeta.primaryKey()) != foreignPk) - { - QDjangoQuerySetPrivate qs(foreignClass); - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, foreignPk)); - qs.sqlFetch(); - if (qs.properties.size() != 1 || !qs.sqlLoad(foreign, 0)) - return 0; - } - return foreign; + const QByteArray prop(name); + QObject *foreign = model->property(prop + "_ptr").value(); + if (!foreign) + return 0; + + // if the foreign object was not loaded yet, do it now + const QString foreignClass = d->foreignFields[prop]; + const QDjangoMetaModel foreignMeta = QDjango::metaModel(foreignClass); + const QVariant foreignPk = model->property(prop + "_id"); + if (foreign->property(foreignMeta.primaryKey()) != foreignPk) + { + QDjangoQuerySetPrivate qs(foreignClass); + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, foreignPk)); + qs.sqlFetch(); + if (qs.properties.size() != 1 || !qs.sqlLoad(foreign, 0)) + return 0; + } + return foreign; } @@ -446,28 +453,28 @@ /*! - Sets the QDjangoModel pointed to by the given foreign-key. - - \param model - \param name - \param value + Sets the QDjangoModel pointed to by the given foreign-key. + + \param model + \param name + \param value - \note The \c model will take ownership of the given \c value. + \note The \c model will take ownership of the given \c value. */ void QDjangoMetaModel::setForeignKey(QObject *model, const char *name, QObject *value) const { - const QByteArray prop(name); - QObject *old = model->property(prop + "_ptr").value(); - if (old == value) - return; - - // store the new pointer and update the foreign key - model->setProperty(prop + "_ptr", qVariantFromValue(value)); - if (value) - { - const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[prop]); - model->setProperty(prop + "_id", value->property(foreignMeta.primaryKey())); - } else { - model->setProperty(prop + "_id", QVariant()); - } + const QByteArray prop(name); + QObject *old = model->property(prop + "_ptr").value(); + if (old == value) + return; + + // store the new pointer and update the foreign key + model->setProperty(prop + "_ptr", qVariantFromValue(value)); + if (value) + { + const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[prop]); + model->setProperty(prop + "_id", value->property(foreignMeta.primaryKey())); + } else { + model->setProperty(prop + "_id", QVariant()); + } } @@ -474,23 +481,23 @@ /*! - Loads the given properties into a \a model instance. + Loads the given properties into a \a model instance. */ void QDjangoMetaModel::load(QObject *model, const QVariantList &properties, int &pos) const { - // process local fields - foreach (const QDjangoMetaField &field, d->localFields) - model->setProperty(field.d->name, properties.at(pos++)); - - // process foreign fields - if (pos >= properties.size()) - return; - foreach (const QByteArray &fkName, d->foreignFields.keys()) - { - QObject *object = model->property(fkName + "_ptr").value(); - if (object) - { - const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[fkName]); - foreignMeta.load(object, properties, pos); - } - } + // process local fields + foreach (const QDjangoMetaField &field, d->localFields) + model->setProperty(field.d->name, properties.at(pos++)); + + // process foreign fields + if (pos >= properties.size()) + return; + foreach (const QByteArray &fkName, d->foreignFields.keys()) + { + QObject *object = model->property(fkName + "_ptr").value(); + if (object) + { + const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[fkName]); + foreignMeta.load(object, properties, pos); + } + } } @@ -497,8 +504,8 @@ /*! - Returns the foreign field mapping. + Returns the foreign field mapping. */ QMap QDjangoMetaModel::foreignFields() const { - return d->foreignFields; + return d->foreignFields; } @@ -505,13 +512,13 @@ /*! - Return the local field with the specified \a name. + Return the local field with the specified \a name. */ QDjangoMetaField QDjangoMetaModel::localField(const QString &name) const { - const QString fieldName = (name == "pk") ? d->primaryKey : name; - foreach (const QDjangoMetaField &field, d->localFields) { - if (field.d->name == fieldName) - return field; - } - return QDjangoMetaField(); + const QString fieldName = (name == "pk") ? d->primaryKey : name; + foreach (const QDjangoMetaField &field, d->localFields) { + if (field.d->name == fieldName) + return field; + } + return QDjangoMetaField(); } @@ -518,8 +525,8 @@ /*! - Returns the list of local fields. + Returns the list of local fields. */ QList QDjangoMetaModel::localFields() const { - return d->localFields; + return d->localFields; } @@ -526,8 +533,8 @@ /*! - Returns the name of the primary key for the current QDjangoMetaModel. + Returns the name of the primary key for the current QDjangoMetaModel. */ QByteArray QDjangoMetaModel::primaryKey() const { - return d->primaryKey; + return d->primaryKey; } @@ -534,8 +541,8 @@ /*! - Returns the name of the database table. + Returns the name of the database table. */ QString QDjangoMetaModel::table() const { - return d->table; + return d->table; } @@ -542,11 +549,11 @@ /*! - Removes the given \a model instance from the database. + Removes the given \a model instance from the database. */ bool QDjangoMetaModel::remove(QObject *model) const { - const QVariant pk = model->property(d->primaryKey); - QDjangoQuerySetPrivate qs(model->metaObject()->className()); - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk)); - return qs.sqlDelete(); + const QVariant pk = model->property(d->primaryKey); + QDjangoQuerySetPrivate qs(model->metaObject()->className()); + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk)); + return qs.sqlDelete(); } @@ -553,57 +560,57 @@ /*! - Saves the given \a model instance to the database. + Saves the given \a model instance to the database. - \return true if saving succeeded, false otherwise + \return true if saving succeeded, false otherwise */ bool QDjangoMetaModel::save(QObject *model) const { - // find primary key - const QDjangoMetaField primaryKey = localField("pk"); - const QVariant pk = model->property(d->primaryKey); - if (!pk.isNull() && !(primaryKey.d->type == QVariant::Int && !pk.toInt())) - { - QSqlDatabase db = QDjango::database(); - QDjangoQuery query(db); - query.prepare(QString("SELECT 1 AS a FROM %1 WHERE %2 = ?").arg( - db.driver()->escapeIdentifier(d->table, QSqlDriver::FieldName), - db.driver()->escapeIdentifier(primaryKey.column(), QSqlDriver::FieldName))); - query.addBindValue(pk); - if (query.exec() && query.next()) - { - // prepare data - QVariantMap fields; - foreach (const QDjangoMetaField &field, d->localFields) { - if (field.d->name != d->primaryKey) { - const QVariant value = model->property(field.d->name); - fields.insert(field.d->name, field.toDatabase(value)); - } - } - - // perform UPDATE - QDjangoQuerySetPrivate qs(model->metaObject()->className()); - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk)); - return qs.sqlUpdate(fields) != -1; - } - } - - // prepare data - QVariantMap fields; - foreach (const QDjangoMetaField &field, d->localFields) { - if (!field.d->autoIncrement) { - const QVariant value = model->property(field.d->name); - fields.insert(field.name(), field.toDatabase(value)); - } - } - - // perform INSERT - QVariant insertId; - QDjangoQuerySetPrivate qs(model->metaObject()->className()); - if (!qs.sqlInsert(fields, &insertId)) - return false; - - // fetch autoincrement pk - if (primaryKey.d->autoIncrement) - model->setProperty(d->primaryKey, insertId); - return true; + // find primary key + const QDjangoMetaField primaryKey = localField("pk"); + const QVariant pk = model->property(d->primaryKey); + if (!pk.isNull() && !(primaryKey.d->type == QVariant::Int && !pk.toInt())) + { + QSqlDatabase db = QDjango::database(); + QDjangoQuery query(db); + query.prepare(QString("SELECT 1 AS a FROM %1 WHERE %2 = ?").arg( + db.driver()->escapeIdentifier(d->table, QSqlDriver::FieldName), + db.driver()->escapeIdentifier(primaryKey.column(), QSqlDriver::FieldName))); + query.addBindValue(pk); + if (query.exec() && query.next()) + { + // prepare data + QVariantMap fields; + foreach (const QDjangoMetaField &field, d->localFields) { + if (field.d->name != d->primaryKey) { + const QVariant value = model->property(field.d->name); + fields.insert(field.d->name, field.toDatabase(value)); + } + } + + // perform UPDATE + QDjangoQuerySetPrivate qs(model->metaObject()->className()); + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk)); + return qs.sqlUpdate(fields) != -1; + } + } + + // prepare data + QVariantMap fields; + foreach (const QDjangoMetaField &field, d->localFields) { + if (!field.d->autoIncrement) { + const QVariant value = model->property(field.d->name); + fields.insert(field.name(), field.toDatabase(value)); + } + } + + // perform INSERT + QVariant insertId; + QDjangoQuerySetPrivate qs(model->metaObject()->className()); + if (!qs.sqlInsert(fields, &insertId)) + return false; + + // fetch autoincrement pk + if (primaryKey.d->autoIncrement) + model->setProperty(d->primaryKey, insertId); + return true; } """]] ## The extracted whitespace changes [[!format diff """ --- a/qdjango-0.2.6/src/db/QDjangoMetaModel.cpp +++ b/qdjango-0.2.6/src/db/QDjangoMetaModel.cpp @@ -231,6 +231,13 @@ QDjangoMetaModel::QDjangoMetaModel(const QObject *model) field.d->db_column = dbColumnOption.isEmpty() ? QString::fromLatin1(field.d->name) : dbColumnOption; field.d->index = true; field.d->null = nullOption; + if (primaryKeyOption) { + field.d->autoIncrement = autoIncrementOption; + field.d->unique = true; + d->primaryKey = field.d->name; + } else if (uniqueOption) { + field.d->unique = true; + } d->localFields << field; continue; } """]]