projects/rohieb.name-repo: somehow a false line from the draft slipped into the instr...
[www-rohieb-name.git] / blag / post / splitting-overly-large-hunks-in-patches / edit-stages.mdwn
1 [[!meta title="Edit stages"]]
2 [[!meta author="rohieb"]]
3 [[!meta license="GNU LGPL, v2.1 or later"]]
4 [[!toc]]
5
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
10 done.
11
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
14 later.
15
16 [QDjango]: https://code.google.com/p/qdjango/
17 [gnu-lgpl]: http://www.gnu.org/licenses/lgpl.html
18
19 ## The original patch
20
21 …with far too much whitespace changes:
22
23 [[!format diff """
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
26 @@ -27,583 +27,590 @@
27 class QDjangoMetaFieldPrivate : public QSharedData
28 {
29 public:
30 - QDjangoMetaFieldPrivate();
31 + QDjangoMetaFieldPrivate();
32
33 - bool autoIncrement;
34 - QString db_column;
35 - QString foreignModel;
36 - bool index;
37 - int maxLength;
38 - QByteArray name;
39 - bool null;
40 - QVariant::Type type;
41 - bool unique;
42 + bool autoIncrement;
43 + QString db_column;
44 + QString foreignModel;
45 + bool index;
46 + int maxLength;
47 + QByteArray name;
48 + bool null;
49 + QVariant::Type type;
50 + bool unique;
51 };
52
53 QDjangoMetaFieldPrivate::QDjangoMetaFieldPrivate()
54 - : autoIncrement(false),
55 - index(false),
56 - maxLength(0),
57 - null(false),
58 - unique(false)
59 + : autoIncrement(false),
60 + index(false),
61 + maxLength(0),
62 + null(false),
63 + unique(false)
64 {
65 }
66
67 /*!
68 - Constructs a new QDjangoMetaField.
69 + Constructs a new QDjangoMetaField.
70 */
71 QDjangoMetaField::QDjangoMetaField()
72 {
73 - d = new QDjangoMetaFieldPrivate;
74 + d = new QDjangoMetaFieldPrivate;
75 }
76
77 /*!
78 - Constructs a copy of \a other.
79 + Constructs a copy of \a other.
80 */
81 QDjangoMetaField::QDjangoMetaField(const QDjangoMetaField &other)
82 - : d(other.d)
83 + : d(other.d)
84 {
85 }
86
87 /*!
88 - Destroys the meta field.
89 + Destroys the meta field.
90 */
91 QDjangoMetaField::~QDjangoMetaField()
92 {
93 }
94
95 /*!
96 - Assigns \a other to this meta field.
97 + Assigns \a other to this meta field.
98 */
99 QDjangoMetaField& QDjangoMetaField::operator=(const QDjangoMetaField& other)
100 {
101 - d = other.d;
102 - return *this;
103 + d = other.d;
104 + return *this;
105 }
106
107 /*!
108 - Returns the database column for this meta field.
109 + Returns the database column for this meta field.
110 */
111 QString QDjangoMetaField::column() const
112 {
113 - return d->db_column;
114 + return d->db_column;
115 }
116
117 /*!
118 - Returns true if this is a valid field.
119 + Returns true if this is a valid field.
120 */
121 bool QDjangoMetaField::isValid() const
122 {
123 - return !d->name.isEmpty();
124 + return !d->name.isEmpty();
125 }
126
127 /*!
128 - Returns name of this meta field.
129 + Returns name of this meta field.
130 */
131 QString QDjangoMetaField::name() const
132 {
133 - return QString::fromLatin1(d->name);
134 + return QString::fromLatin1(d->name);
135 }
136
137 /*!
138 - Transforms the given field value for database storage.
139 + Transforms the given field value for database storage.
140 */
141 QVariant QDjangoMetaField::toDatabase(const QVariant &value) const
142 {
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
147 - return QVariant();
148 - } else
149 - return value;
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
154 + return QVariant();
155 + } else
156 + return value;
157 }
158
159 static QMap<QString, QString> parseOptions(const char *value)
160 {
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];
167 - } else {
168 - qWarning() << "Could not parse option" << item;
169 - }
170 - }
171 - return options;
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];
178 + } else {
179 + qWarning() << "Could not parse option" << item;
180 + }
181 + }
182 + return options;
183 }
184
185 class QDjangoMetaModelPrivate : public QSharedData
186 {
187 public:
188 - QList<QDjangoMetaField> localFields;
189 - QMap<QByteArray, QString> foreignFields;
190 - QByteArray primaryKey;
191 - QString table;
192 + QList<QDjangoMetaField> localFields;
193 + QMap<QByteArray, QString> foreignFields;
194 + QByteArray primaryKey;
195 + QString table;
196 };
197
198 /*!
199 - Constructs a new QDjangoMetaModel by inspecting the given \a model instance.
200 + Constructs a new QDjangoMetaModel by inspecting the given \a model instance.
201 */
202 QDjangoMetaModel::QDjangoMetaModel(const QObject *model)
203 - : d(new QDjangoMetaModelPrivate)
204 + : d(new QDjangoMetaModelPrivate)
205 {
206 - if (!model)
207 - return;
208 + if (!model)
209 + return;
210
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();
215 +
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()) {
222 + option.next();
223 + if (option.key() == "db_table")
224 + d->table = option.value();
225 + }
226 + }
227 +
228 + const int count = meta->propertyCount();
229 + for(int i = QObject::staticMetaObject.propertyCount(); i < count; ++i)
230 + {
231 + QString typeName = meta->property(i).typeName();
232 + if (!qstrcmp(meta->property(i).name(), "pk"))
233 + continue;
234 +
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)
246 + {
247 + QMap<QString, QString> options = parseOptions(meta->classInfo(infoIndex).value());
248 + QMapIterator<QString, QString> option(options);
249 + while (option.hasNext()) {
250 + option.next();
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");
268 + }
269 + }
270 +
271 + // ignore field
272 + if (ignoreFieldOption)
273 + continue;
274 +
275 + // foreign field
276 + if (typeName.endsWith("*"))
277 + {
278 + const QByteArray fkName = meta->property(i).name();
279 + const QString fkModel = typeName.left(typeName.size() - 1);
280 + d->foreignFields.insert(fkName, fkModel);
281 +
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;
298 + }
299 + d->localFields << field;
300 + continue;
301 + }
302 +
303 + // local 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;
317 + }
318 +
319 + d->localFields << field;
320 + }
321 +
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;
332 + }
333
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()) {
340 - option.next();
341 - if (option.key() == "db_table")
342 - d->table = option.value();
343 - }
344 - }
345 -
346 - const int count = meta->propertyCount();
347 - for(int i = QObject::staticMetaObject.propertyCount(); i < count; ++i)
348 - {
349 - QString typeName = meta->property(i).typeName();
350 - if (!qstrcmp(meta->property(i).name(), "pk"))
351 - continue;
352 -
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)
364 - {
365 - QMap<QString, QString> options = parseOptions(meta->classInfo(infoIndex).value());
366 - QMapIterator<QString, QString> option(options);
367 - while (option.hasNext()) {
368 - option.next();
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");
386 - }
387 - }
388 -
389 - // ignore field
390 - if (ignoreFieldOption)
391 - continue;
392 -
393 - // foreign field
394 - if (typeName.endsWith("*"))
395 - {
396 - const QByteArray fkName = meta->property(i).name();
397 - const QString fkModel = typeName.left(typeName.size() - 1);
398 - d->foreignFields.insert(fkName, fkModel);
399 -
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;
411 - continue;
412 - }
413 -
414 - // local 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;
428 - }
429 -
430 - d->localFields << field;
431 - }
432 -
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;
443 - }
444 -
445 }
446
447 /*!
448 - Constructs a copy of \a other.
449 + Constructs a copy of \a other.
450 */
451 QDjangoMetaModel::QDjangoMetaModel(const QDjangoMetaModel &other)
452 - : d(other.d)
453 + : d(other.d)
454 {
455 }
456
457 /*!
458 - Destroys the meta model.
459 + Destroys the meta model.
460 */
461 QDjangoMetaModel::~QDjangoMetaModel()
462 {
463 }
464
465 /*!
466 - Assigns \a other to this meta model.
467 + Assigns \a other to this meta model.
468 */
469 QDjangoMetaModel& QDjangoMetaModel::operator=(const QDjangoMetaModel& other)
470 {
471 - d = other.d;
472 - return *this;
473 + d = other.d;
474 + return *this;
475 }
476
477 /*!
478 - Creates the database table for this QDjangoMetaModel.
479 + Creates the database table for this QDjangoMetaModel.
480 */
481 bool QDjangoMetaModel::createTable() const
482 {
483 - QSqlDatabase db = QDjango::database();
484 - QSqlDriver *driver = db.driver();
485 - const QString driverName = db.driverName();
486 -
487 - QStringList propSql;
488 - const QString quotedTable = db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName);
489 - foreach (const QDjangoMetaField &field, d->localFields)
490 - {
491 - QString fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName);
492 - switch (field.d->type) {
493 - case QVariant::Bool:
494 - fieldSql += " BOOLEAN";
495 - break;
496 - case QVariant::ByteArray:
497 - if (driverName == QLatin1String("QPSQL"))
498 - fieldSql += " BYTEA";
499 - else {
500 - fieldSql += " BLOB";
501 - if (field.d->maxLength > 0)
502 - fieldSql += QString("(%1)").arg(field.d->maxLength);
503 - }
504 - break;
505 - case QVariant::Date:
506 - fieldSql += " DATE";
507 - break;
508 - case QVariant::DateTime:
509 - if (driverName == QLatin1String("QPSQL"))
510 - fieldSql += " TIMESTAMP";
511 - else
512 - fieldSql += " DATETIME";
513 - break;
514 - case QVariant::Double:
515 - fieldSql += " REAL";
516 - break;
517 - case QVariant::Int:
518 - fieldSql += " INTEGER";
519 - break;
520 - case QVariant::LongLong:
521 - fieldSql += " BIGINT";
522 - break;
523 - case QVariant::String:
524 - if (field.d->maxLength > 0)
525 - fieldSql += QString(" VARCHAR(%1)").arg(field.d->maxLength);
526 - else
527 - fieldSql += " TEXT";
528 - break;
529 - case QVariant::Time:
530 - fieldSql += " TIME";
531 - break;
532 - default:
533 - qWarning() << "Unhandled type" << field.d->type << "for property" << field.d->name;
534 - continue;
535 - }
536 -
537 - if (!field.d->null)
538 - fieldSql += " NOT NULL";
539 -
540 - // primary key
541 - if (field.d->name == d->primaryKey)
542 - fieldSql += " PRIMARY KEY";
543 -
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";
553 - }
554 -
555 - // foreign key
556 - if (!field.d->foreignModel.isEmpty())
557 - {
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));
563 - }
564 - propSql << fieldSql;
565 - }
566 -
567 - // create table
568 - QDjangoQuery createQuery(db);
569 - if (!createQuery.exec(QString("CREATE TABLE %1 (%2)").arg(
570 - quotedTable,
571 - propSql.join(", "))))
572 - return false;
573 -
574 - // create indices
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),
581 - quotedTable,
582 - driver->escapeIdentifier(field.column(), QSqlDriver::FieldName))))
583 - return false;
584 - }
585 - }
586 + QSqlDatabase db = QDjango::database();
587 + QSqlDriver *driver = db.driver();
588 + const QString driverName = db.driverName();
589 +
590 + QStringList propSql;
591 + const QString quotedTable = db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName);
592 + foreach (const QDjangoMetaField &field, d->localFields)
593 + {
594 + QString fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName);
595 + switch (field.d->type) {
596 + case QVariant::Bool:
597 + fieldSql += " BOOLEAN";
598 + break;
599 + case QVariant::ByteArray:
600 + if (driverName == QLatin1String("QPSQL"))
601 + fieldSql += " BYTEA";
602 + else {
603 + fieldSql += " BLOB";
604 + if (field.d->maxLength > 0)
605 + fieldSql += QString("(%1)").arg(field.d->maxLength);
606 + }
607 + break;
608 + case QVariant::Date:
609 + fieldSql += " DATE";
610 + break;
611 + case QVariant::DateTime:
612 + if (driverName == QLatin1String("QPSQL"))
613 + fieldSql += " TIMESTAMP";
614 + else
615 + fieldSql += " DATETIME";
616 + break;
617 + case QVariant::Double:
618 + fieldSql += " REAL";
619 + break;
620 + case QVariant::Int:
621 + fieldSql += " INTEGER";
622 + break;
623 + case QVariant::LongLong:
624 + fieldSql += " BIGINT";
625 + break;
626 + case QVariant::String:
627 + if (field.d->maxLength > 0)
628 + fieldSql += QString(" VARCHAR(%1)").arg(field.d->maxLength);
629 + else
630 + fieldSql += " TEXT";
631 + break;
632 + case QVariant::Time:
633 + fieldSql += " TIME";
634 + break;
635 + default:
636 + qWarning() << "Unhandled type" << field.d->type << "for property" << field.d->name;
637 + continue;
638 + }
639 +
640 + if (!field.d->null)
641 + fieldSql += " NOT NULL";
642 +
643 + // primary key
644 + if (field.d->name == d->primaryKey)
645 + fieldSql += " PRIMARY KEY";
646 +
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";
656 + }
657 +
658 + // foreign key
659 + if (!field.d->foreignModel.isEmpty())
660 + {
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));
666 + }
667 + propSql << fieldSql;
668 + }
669 +
670 + // create table
671 + QDjangoQuery createQuery(db);
672 + if (!createQuery.exec(QString("CREATE TABLE %1 (%2)").arg(
673 + quotedTable,
674 + propSql.join(", "))))
675 + return false;
676 +
677 + // create indices
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),
684 + quotedTable,
685 + driver->escapeIdentifier(field.column(), QSqlDriver::FieldName))))
686 + return false;
687 + }
688 + }
689
690 - return true;
691 + return true;
692 }
693
694 /*!
695 - Drops the database table for this QDjangoMetaModel.
696 + Drops the database table for this QDjangoMetaModel.
697 */
698 bool QDjangoMetaModel::dropTable() const
699 {
700 - QSqlDatabase db = QDjango::database();
701 + QSqlDatabase db = QDjango::database();
702
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)));
709 }
710
711 /*!
712 - Retrieves the QDjangoModel pointed to by the given foreign-key.
713 + Retrieves the QDjangoModel pointed to by the given foreign-key.
714
715 - \param model
716 - \param name
717 + \param model
718 + \param name
719 */
720 QObject *QDjangoMetaModel::foreignKey(const QObject *model, const char *name) const
721 {
722 - const QByteArray prop(name);
723 - QObject *foreign = model->property(prop + "_ptr").value<QObject*>();
724 - if (!foreign)
725 - return 0;
726 -
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)
732 - {
733 - QDjangoQuerySetPrivate qs(foreignClass);
734 - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, foreignPk));
735 - qs.sqlFetch();
736 - if (qs.properties.size() != 1 || !qs.sqlLoad(foreign, 0))
737 - return 0;
738 - }
739 - return foreign;
740 + const QByteArray prop(name);
741 + QObject *foreign = model->property(prop + "_ptr").value<QObject*>();
742 + if (!foreign)
743 + return 0;
744 +
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)
750 + {
751 + QDjangoQuerySetPrivate qs(foreignClass);
752 + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, foreignPk));
753 + qs.sqlFetch();
754 + if (qs.properties.size() != 1 || !qs.sqlLoad(foreign, 0))
755 + return 0;
756 + }
757 + return foreign;
758 }
759
760 /*!
761 - Sets the QDjangoModel pointed to by the given foreign-key.
762 -
763 - \param model
764 - \param name
765 - \param value
766 + Sets the QDjangoModel pointed to by the given foreign-key.
767 +
768 + \param model
769 + \param name
770 + \param value
771
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.
774 */
775 void QDjangoMetaModel::setForeignKey(QObject *model, const char *name, QObject *value) const
776 {
777 - const QByteArray prop(name);
778 - QObject *old = model->property(prop + "_ptr").value<QObject*>();
779 - if (old == value)
780 - return;
781 -
782 - // store the new pointer and update the foreign key
783 - model->setProperty(prop + "_ptr", qVariantFromValue(value));
784 - if (value)
785 - {
786 - const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[prop]);
787 - model->setProperty(prop + "_id", value->property(foreignMeta.primaryKey()));
788 - } else {
789 - model->setProperty(prop + "_id", QVariant());
790 - }
791 + const QByteArray prop(name);
792 + QObject *old = model->property(prop + "_ptr").value<QObject*>();
793 + if (old == value)
794 + return;
795 +
796 + // store the new pointer and update the foreign key
797 + model->setProperty(prop + "_ptr", qVariantFromValue(value));
798 + if (value)
799 + {
800 + const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[prop]);
801 + model->setProperty(prop + "_id", value->property(foreignMeta.primaryKey()));
802 + } else {
803 + model->setProperty(prop + "_id", QVariant());
804 + }
805 }
806
807 /*!
808 - Loads the given properties into a \a model instance.
809 + Loads the given properties into a \a model instance.
810 */
811 void QDjangoMetaModel::load(QObject *model, const QVariantList &properties, int &pos) const
812 {
813 - // process local fields
814 - foreach (const QDjangoMetaField &field, d->localFields)
815 - model->setProperty(field.d->name, properties.at(pos++));
816 -
817 - // process foreign fields
818 - if (pos >= properties.size())
819 - return;
820 - foreach (const QByteArray &fkName, d->foreignFields.keys())
821 - {
822 - QObject *object = model->property(fkName + "_ptr").value<QObject*>();
823 - if (object)
824 - {
825 - const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[fkName]);
826 - foreignMeta.load(object, properties, pos);
827 - }
828 - }
829 + // process local fields
830 + foreach (const QDjangoMetaField &field, d->localFields)
831 + model->setProperty(field.d->name, properties.at(pos++));
832 +
833 + // process foreign fields
834 + if (pos >= properties.size())
835 + return;
836 + foreach (const QByteArray &fkName, d->foreignFields.keys())
837 + {
838 + QObject *object = model->property(fkName + "_ptr").value<QObject*>();
839 + if (object)
840 + {
841 + const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[fkName]);
842 + foreignMeta.load(object, properties, pos);
843 + }
844 + }
845 }
846
847 /*!
848 - Returns the foreign field mapping.
849 + Returns the foreign field mapping.
850 */
851 QMap<QByteArray, QString> QDjangoMetaModel::foreignFields() const
852 {
853 - return d->foreignFields;
854 + return d->foreignFields;
855 }
856
857 /*!
858 - Return the local field with the specified \a name.
859 + Return the local field with the specified \a name.
860 */
861 QDjangoMetaField QDjangoMetaModel::localField(const QString &name) const
862 {
863 - const QString fieldName = (name == "pk") ? d->primaryKey : name;
864 - foreach (const QDjangoMetaField &field, d->localFields) {
865 - if (field.d->name == fieldName)
866 - return field;
867 - }
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)
872 + return field;
873 + }
874 + return QDjangoMetaField();
875 }
876
877 /*!
878 - Returns the list of local fields.
879 + Returns the list of local fields.
880 */
881 QList<QDjangoMetaField> QDjangoMetaModel::localFields() const
882 {
883 - return d->localFields;
884 + return d->localFields;
885 }
886
887 /*!
888 - Returns the name of the primary key for the current QDjangoMetaModel.
889 + Returns the name of the primary key for the current QDjangoMetaModel.
890 */
891 QByteArray QDjangoMetaModel::primaryKey() const
892 {
893 - return d->primaryKey;
894 + return d->primaryKey;
895 }
896
897 /*!
898 - Returns the name of the database table.
899 + Returns the name of the database table.
900 */
901 QString QDjangoMetaModel::table() const
902 {
903 - return d->table;
904 + return d->table;
905 }
906
907 /*!
908 - Removes the given \a model instance from the database.
909 + Removes the given \a model instance from the database.
910 */
911 bool QDjangoMetaModel::remove(QObject *model) const
912 {
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();
921 }
922
923 /*!
924 - Saves the given \a model instance to the database.
925 + Saves the given \a model instance to the database.
926
927 - \return true if saving succeeded, false otherwise
928 + \return true if saving succeeded, false otherwise
929 */
930 bool QDjangoMetaModel::save(QObject *model) const
931 {
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()))
936 - {
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())
944 - {
945 - // prepare data
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));
951 - }
952 - }
953 -
954 - // perform UPDATE
955 - QDjangoQuerySetPrivate qs(model->metaObject()->className());
956 - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk));
957 - return qs.sqlUpdate(fields) != -1;
958 - }
959 - }
960 -
961 - // prepare data
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));
967 - }
968 - }
969 -
970 - // perform INSERT
971 - QVariant insertId;
972 - QDjangoQuerySetPrivate qs(model->metaObject()->className());
973 - if (!qs.sqlInsert(fields, &insertId))
974 - return false;
975 -
976 - // fetch autoincrement pk
977 - if (primaryKey.d->autoIncrement)
978 - model->setProperty(d->primaryKey, insertId);
979 - return true;
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()))
984 + {
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())
992 + {
993 + // prepare data
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));
999 + }
1000 + }
1001 +
1002 + // perform UPDATE
1003 + QDjangoQuerySetPrivate qs(model->metaObject()->className());
1004 + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk));
1005 + return qs.sqlUpdate(fields) != -1;
1006 + }
1007 + }
1008 +
1009 + // prepare data
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));
1015 + }
1016 + }
1017 +
1018 + // perform INSERT
1019 + QVariant insertId;
1020 + QDjangoQuerySetPrivate qs(model->metaObject()->className());
1021 + if (!qs.sqlInsert(fields, &insertId))
1022 + return false;
1023 +
1024 + // fetch autoincrement pk
1025 + if (primaryKey.d->autoIncrement)
1026 + model->setProperty(d->primaryKey, insertId);
1027 + return true;
1028 }
1029 """]]
1030
1031
1032 ## After editing with Emacs
1033
1034 Note the additional `@@ ... @@` lines:
1035 [[!format diff """
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
1038 @@ -27,15 +27,15 @@
1039 class QDjangoMetaFieldPrivate : public QSharedData
1040 {
1041 public:
1042 - QDjangoMetaFieldPrivate();
1043 + QDjangoMetaFieldPrivate();
1044
1045 - bool autoIncrement;
1046 - QString db_column;
1047 - QString foreignModel;
1048 - bool index;
1049 - int maxLength;
1050 - QByteArray name;
1051 - bool null;
1052 - QVariant::Type type;
1053 - bool unique;
1054 + bool autoIncrement;
1055 + QString db_column;
1056 + QString foreignModel;
1057 + bool index;
1058 + int maxLength;
1059 + QByteArray name;
1060 + bool null;
1061 + QVariant::Type type;
1062 + bool unique;
1063 };
1064 @@ -42,9 +42,9 @@
1065
1066 QDjangoMetaFieldPrivate::QDjangoMetaFieldPrivate()
1067 - : autoIncrement(false),
1068 - index(false),
1069 - maxLength(0),
1070 - null(false),
1071 - unique(false)
1072 + : autoIncrement(false),
1073 + index(false),
1074 + maxLength(0),
1075 + null(false),
1076 + unique(false)
1077 {
1078 }
1079 @@ -51,8 +51,8 @@
1080
1081 /*!
1082 - Constructs a new QDjangoMetaField.
1083 + Constructs a new QDjangoMetaField.
1084 */
1085 QDjangoMetaField::QDjangoMetaField()
1086 {
1087 - d = new QDjangoMetaFieldPrivate;
1088 + d = new QDjangoMetaFieldPrivate;
1089 }
1090 @@ -59,8 +59,8 @@
1091
1092 /*!
1093 - Constructs a copy of \a other.
1094 + Constructs a copy of \a other.
1095 */
1096 QDjangoMetaField::QDjangoMetaField(const QDjangoMetaField &other)
1097 - : d(other.d)
1098 + : d(other.d)
1099 {
1100 }
1101 @@ -67,7 +67,7 @@
1102
1103 /*!
1104 - Destroys the meta field.
1105 + Destroys the meta field.
1106 */
1107 QDjangoMetaField::~QDjangoMetaField()
1108 {
1109 }
1110 @@ -74,9 +74,9 @@
1111
1112 /*!
1113 - Assigns \a other to this meta field.
1114 + Assigns \a other to this meta field.
1115 */
1116 QDjangoMetaField& QDjangoMetaField::operator=(const QDjangoMetaField& other)
1117 {
1118 - d = other.d;
1119 - return *this;
1120 + d = other.d;
1121 + return *this;
1122 }
1123 @@ -83,8 +83,8 @@
1124
1125 /*!
1126 - Returns the database column for this meta field.
1127 + Returns the database column for this meta field.
1128 */
1129 QString QDjangoMetaField::column() const
1130 {
1131 - return d->db_column;
1132 + return d->db_column;
1133 }
1134 @@ -91,8 +91,8 @@
1135
1136 /*!
1137 - Returns true if this is a valid field.
1138 + Returns true if this is a valid field.
1139 */
1140 bool QDjangoMetaField::isValid() const
1141 {
1142 - return !d->name.isEmpty();
1143 + return !d->name.isEmpty();
1144 }
1145 @@ -99,8 +99,8 @@
1146
1147 /*!
1148 - Returns name of this meta field.
1149 + Returns name of this meta field.
1150 */
1151 QString QDjangoMetaField::name() const
1152 {
1153 - return QString::fromLatin1(d->name);
1154 + return QString::fromLatin1(d->name);
1155 }
1156 @@ -107,14 +107,14 @@
1157
1158 /*!
1159 - Transforms the given field value for database storage.
1160 + Transforms the given field value for database storage.
1161 */
1162 QVariant QDjangoMetaField::toDatabase(const QVariant &value) const
1163 {
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();
1169 - } else
1170 - return value;
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();
1176 + } else
1177 + return value;
1178 }
1179 @@ -121,15 +121,15 @@
1180
1181 static QMap<QString, QString> parseOptions(const char *value)
1182 {
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];
1189 - } else {
1190 - qWarning() << "Could not parse option" << item;
1191 - }
1192 - }
1193 - return options;
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];
1200 + } else {
1201 + qWarning() << "Could not parse option" << item;
1202 + }
1203 + }
1204 + return options;
1205 }
1206 @@ -136,12 +136,12 @@
1207
1208 class QDjangoMetaModelPrivate : public QSharedData
1209 {
1210 public:
1211 - QList<QDjangoMetaField> localFields;
1212 - QMap<QByteArray, QString> foreignFields;
1213 - QByteArray primaryKey;
1214 - QString table;
1215 + QList<QDjangoMetaField> localFields;
1216 + QMap<QByteArray, QString> foreignFields;
1217 + QByteArray primaryKey;
1218 + QString table;
1219 };
1220
1221 /*!
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 @@
1225 */
1226 QDjangoMetaModel::QDjangoMetaModel(const QObject *model)
1227 - : d(new QDjangoMetaModelPrivate)
1228 + : d(new QDjangoMetaModelPrivate)
1229 {
1230 - if (!model)
1231 - return;
1232 + if (!model)
1233 + return;
1234
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();
1239 +
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()) {
1246 + option.next();
1247 + if (option.key() == "db_table")
1248 + d->table = option.value();
1249 + }
1250 + }
1251 +
1252 + const int count = meta->propertyCount();
1253 + for(int i = QObject::staticMetaObject.propertyCount(); i < count; ++i)
1254 + {
1255 + QString typeName = meta->property(i).typeName();
1256 + if (!qstrcmp(meta->property(i).name(), "pk"))
1257 + continue;
1258 +
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)
1270 + {
1271 + QMap<QString, QString> options = parseOptions(meta->classInfo(infoIndex).value());
1272 + QMapIterator<QString, QString> option(options);
1273 + while (option.hasNext()) {
1274 + option.next();
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");
1292 + }
1293 + }
1294 +
1295 + // ignore field
1296 + if (ignoreFieldOption)
1297 + continue;
1298 +
1299 + // foreign field
1300 + if (typeName.endsWith("*"))
1301 + {
1302 + const QByteArray fkName = meta->property(i).name();
1303 + const QString fkModel = typeName.left(typeName.size() - 1);
1304 + d->foreignFields.insert(fkName, fkModel);
1305 +
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;
1322 + }
1323 + d->localFields << field;
1324 + continue;
1325 + }
1326 +
1327 + // local 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;
1341 + }
1342 +
1343 + d->localFields << field;
1344 + }
1345 +
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;
1356 + }
1357
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()) {
1364 - option.next();
1365 - if (option.key() == "db_table")
1366 - d->table = option.value();
1367 - }
1368 - }
1369 -
1370 - const int count = meta->propertyCount();
1371 - for(int i = QObject::staticMetaObject.propertyCount(); i < count; ++i)
1372 - {
1373 - QString typeName = meta->property(i).typeName();
1374 - if (!qstrcmp(meta->property(i).name(), "pk"))
1375 - continue;
1376 -
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)
1388 - {
1389 - QMap<QString, QString> options = parseOptions(meta->classInfo(infoIndex).value());
1390 - QMapIterator<QString, QString> option(options);
1391 - while (option.hasNext()) {
1392 - option.next();
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");
1410 - }
1411 - }
1412 -
1413 - // ignore field
1414 - if (ignoreFieldOption)
1415 - continue;
1416 -
1417 - // foreign field
1418 - if (typeName.endsWith("*"))
1419 - {
1420 - const QByteArray fkName = meta->property(i).name();
1421 - const QString fkModel = typeName.left(typeName.size() - 1);
1422 - d->foreignFields.insert(fkName, fkModel);
1423 -
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;
1435 - continue;
1436 - }
1437 -
1438 - // local 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;
1452 - }
1453 -
1454 - d->localFields << field;
1455 - }
1456 -
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;
1467 - }
1468 -
1469 }
1470
1471 /*!
1472 - Constructs a copy of \a other.
1473 + Constructs a copy of \a other.
1474 */
1475 QDjangoMetaModel::QDjangoMetaModel(const QDjangoMetaModel &other)
1476 - : d(other.d)
1477 + : d(other.d)
1478 {
1479 }
1480
1481 /*!
1482 - Destroys the meta model.
1483 + Destroys the meta model.
1484 */
1485 QDjangoMetaModel::~QDjangoMetaModel()
1486 {
1487 }
1488
1489 /*!
1490 - Assigns \a other to this meta model.
1491 + Assigns \a other to this meta model.
1492 */
1493 @@ -289,6 +296,6 @@
1494 QDjangoMetaModel& QDjangoMetaModel::operator=(const QDjangoMetaModel& other)
1495 {
1496 - d = other.d;
1497 - return *this;
1498 + d = other.d;
1499 + return *this;
1500 }
1501
1502 @@ -295,112 +302,112 @@
1503 /*!
1504 - Creates the database table for this QDjangoMetaModel.
1505 + Creates the database table for this QDjangoMetaModel.
1506 */
1507 bool QDjangoMetaModel::createTable() const
1508 {
1509 - QSqlDatabase db = QDjango::database();
1510 - QSqlDriver *driver = db.driver();
1511 - const QString driverName = db.driverName();
1512 -
1513 - QStringList propSql;
1514 - const QString quotedTable = db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName);
1515 - foreach (const QDjangoMetaField &field, d->localFields)
1516 - {
1517 - QString fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName);
1518 - switch (field.d->type) {
1519 - case QVariant::Bool:
1520 - fieldSql += " BOOLEAN";
1521 - break;
1522 - case QVariant::ByteArray:
1523 - if (driverName == QLatin1String("QPSQL"))
1524 - fieldSql += " BYTEA";
1525 - else {
1526 - fieldSql += " BLOB";
1527 - if (field.d->maxLength > 0)
1528 - fieldSql += QString("(%1)").arg(field.d->maxLength);
1529 - }
1530 - break;
1531 - case QVariant::Date:
1532 - fieldSql += " DATE";
1533 - break;
1534 - case QVariant::DateTime:
1535 - if (driverName == QLatin1String("QPSQL"))
1536 - fieldSql += " TIMESTAMP";
1537 - else
1538 - fieldSql += " DATETIME";
1539 - break;
1540 - case QVariant::Double:
1541 - fieldSql += " REAL";
1542 - break;
1543 - case QVariant::Int:
1544 - fieldSql += " INTEGER";
1545 - break;
1546 - case QVariant::LongLong:
1547 - fieldSql += " BIGINT";
1548 - break;
1549 - case QVariant::String:
1550 - if (field.d->maxLength > 0)
1551 - fieldSql += QString(" VARCHAR(%1)").arg(field.d->maxLength);
1552 - else
1553 - fieldSql += " TEXT";
1554 - break;
1555 - case QVariant::Time:
1556 - fieldSql += " TIME";
1557 - break;
1558 - default:
1559 - qWarning() << "Unhandled type" << field.d->type << "for property" << field.d->name;
1560 - continue;
1561 - }
1562 -
1563 - if (!field.d->null)
1564 - fieldSql += " NOT NULL";
1565 -
1566 - // primary key
1567 - if (field.d->name == d->primaryKey)
1568 - fieldSql += " PRIMARY KEY";
1569 -
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";
1579 - }
1580 -
1581 - // foreign key
1582 - if (!field.d->foreignModel.isEmpty())
1583 - {
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));
1589 - }
1590 - propSql << fieldSql;
1591 - }
1592 -
1593 - // create table
1594 - QDjangoQuery createQuery(db);
1595 - if (!createQuery.exec(QString("CREATE TABLE %1 (%2)").arg(
1596 - quotedTable,
1597 - propSql.join(", "))))
1598 - return false;
1599 -
1600 - // create indices
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),
1607 - quotedTable,
1608 - driver->escapeIdentifier(field.column(), QSqlDriver::FieldName))))
1609 - return false;
1610 - }
1611 - }
1612 + QSqlDatabase db = QDjango::database();
1613 + QSqlDriver *driver = db.driver();
1614 + const QString driverName = db.driverName();
1615 +
1616 + QStringList propSql;
1617 + const QString quotedTable = db.driver()->escapeIdentifier(d->table, QSqlDriver::TableName);
1618 + foreach (const QDjangoMetaField &field, d->localFields)
1619 + {
1620 + QString fieldSql = driver->escapeIdentifier(field.column(), QSqlDriver::FieldName);
1621 + switch (field.d->type) {
1622 + case QVariant::Bool:
1623 + fieldSql += " BOOLEAN";
1624 + break;
1625 + case QVariant::ByteArray:
1626 + if (driverName == QLatin1String("QPSQL"))
1627 + fieldSql += " BYTEA";
1628 + else {
1629 + fieldSql += " BLOB";
1630 + if (field.d->maxLength > 0)
1631 + fieldSql += QString("(%1)").arg(field.d->maxLength);
1632 + }
1633 + break;
1634 + case QVariant::Date:
1635 + fieldSql += " DATE";
1636 + break;
1637 + case QVariant::DateTime:
1638 + if (driverName == QLatin1String("QPSQL"))
1639 + fieldSql += " TIMESTAMP";
1640 + else
1641 + fieldSql += " DATETIME";
1642 + break;
1643 + case QVariant::Double:
1644 + fieldSql += " REAL";
1645 + break;
1646 + case QVariant::Int:
1647 + fieldSql += " INTEGER";
1648 + break;
1649 + case QVariant::LongLong:
1650 + fieldSql += " BIGINT";
1651 + break;
1652 + case QVariant::String:
1653 + if (field.d->maxLength > 0)
1654 + fieldSql += QString(" VARCHAR(%1)").arg(field.d->maxLength);
1655 + else
1656 + fieldSql += " TEXT";
1657 + break;
1658 + case QVariant::Time:
1659 + fieldSql += " TIME";
1660 + break;
1661 + default:
1662 + qWarning() << "Unhandled type" << field.d->type << "for property" << field.d->name;
1663 + continue;
1664 + }
1665 +
1666 + if (!field.d->null)
1667 + fieldSql += " NOT NULL";
1668 +
1669 + // primary key
1670 + if (field.d->name == d->primaryKey)
1671 + fieldSql += " PRIMARY KEY";
1672 +
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";
1682 + }
1683 +
1684 + // foreign key
1685 + if (!field.d->foreignModel.isEmpty())
1686 + {
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));
1692 + }
1693 + propSql << fieldSql;
1694 + }
1695 +
1696 + // create table
1697 + QDjangoQuery createQuery(db);
1698 + if (!createQuery.exec(QString("CREATE TABLE %1 (%2)").arg(
1699 + quotedTable,
1700 + propSql.join(", "))))
1701 + return false;
1702 +
1703 + // create indices
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),
1710 + quotedTable,
1711 + driver->escapeIdentifier(field.column(), QSqlDriver::FieldName))))
1712 + return false;
1713 + }
1714 + }
1715
1716 - return true;
1717 + return true;
1718 }
1719
1720 @@ -407,12 +414,12 @@
1721 /*!
1722 - Drops the database table for this QDjangoMetaModel.
1723 + Drops the database table for this QDjangoMetaModel.
1724 */
1725 bool QDjangoMetaModel::dropTable() const
1726 {
1727 - QSqlDatabase db = QDjango::database();
1728 + QSqlDatabase db = QDjango::database();
1729
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)));
1736 }
1737
1738 @@ -419,27 +426,27 @@
1739 /*!
1740 - Retrieves the QDjangoModel pointed to by the given foreign-key.
1741 + Retrieves the QDjangoModel pointed to by the given foreign-key.
1742
1743 - \param model
1744 - \param name
1745 + \param model
1746 + \param name
1747 */
1748 QObject *QDjangoMetaModel::foreignKey(const QObject *model, const char *name) const
1749 {
1750 - const QByteArray prop(name);
1751 - QObject *foreign = model->property(prop + "_ptr").value<QObject*>();
1752 - if (!foreign)
1753 - return 0;
1754 -
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)
1760 - {
1761 - QDjangoQuerySetPrivate qs(foreignClass);
1762 - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, foreignPk));
1763 - qs.sqlFetch();
1764 - if (qs.properties.size() != 1 || !qs.sqlLoad(foreign, 0))
1765 - return 0;
1766 - }
1767 - return foreign;
1768 + const QByteArray prop(name);
1769 + QObject *foreign = model->property(prop + "_ptr").value<QObject*>();
1770 + if (!foreign)
1771 + return 0;
1772 +
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)
1778 + {
1779 + QDjangoQuerySetPrivate qs(foreignClass);
1780 + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, foreignPk));
1781 + qs.sqlFetch();
1782 + if (qs.properties.size() != 1 || !qs.sqlLoad(foreign, 0))
1783 + return 0;
1784 + }
1785 + return foreign;
1786 }
1787 @@ -446,28 +453,28 @@
1788
1789 /*!
1790 - Sets the QDjangoModel pointed to by the given foreign-key.
1791 -
1792 - \param model
1793 - \param name
1794 - \param value
1795 + Sets the QDjangoModel pointed to by the given foreign-key.
1796 +
1797 + \param model
1798 + \param name
1799 + \param value
1800
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.
1803 */
1804 void QDjangoMetaModel::setForeignKey(QObject *model, const char *name, QObject *value) const
1805 {
1806 - const QByteArray prop(name);
1807 - QObject *old = model->property(prop + "_ptr").value<QObject*>();
1808 - if (old == value)
1809 - return;
1810 -
1811 - // store the new pointer and update the foreign key
1812 - model->setProperty(prop + "_ptr", qVariantFromValue(value));
1813 - if (value)
1814 - {
1815 - const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[prop]);
1816 - model->setProperty(prop + "_id", value->property(foreignMeta.primaryKey()));
1817 - } else {
1818 - model->setProperty(prop + "_id", QVariant());
1819 - }
1820 + const QByteArray prop(name);
1821 + QObject *old = model->property(prop + "_ptr").value<QObject*>();
1822 + if (old == value)
1823 + return;
1824 +
1825 + // store the new pointer and update the foreign key
1826 + model->setProperty(prop + "_ptr", qVariantFromValue(value));
1827 + if (value)
1828 + {
1829 + const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[prop]);
1830 + model->setProperty(prop + "_id", value->property(foreignMeta.primaryKey()));
1831 + } else {
1832 + model->setProperty(prop + "_id", QVariant());
1833 + }
1834 }
1835
1836 @@ -474,23 +481,23 @@
1837 /*!
1838 - Loads the given properties into a \a model instance.
1839 + Loads the given properties into a \a model instance.
1840 */
1841 void QDjangoMetaModel::load(QObject *model, const QVariantList &properties, int &pos) const
1842 {
1843 - // process local fields
1844 - foreach (const QDjangoMetaField &field, d->localFields)
1845 - model->setProperty(field.d->name, properties.at(pos++));
1846 -
1847 - // process foreign fields
1848 - if (pos >= properties.size())
1849 - return;
1850 - foreach (const QByteArray &fkName, d->foreignFields.keys())
1851 - {
1852 - QObject *object = model->property(fkName + "_ptr").value<QObject*>();
1853 - if (object)
1854 - {
1855 - const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[fkName]);
1856 - foreignMeta.load(object, properties, pos);
1857 - }
1858 - }
1859 + // process local fields
1860 + foreach (const QDjangoMetaField &field, d->localFields)
1861 + model->setProperty(field.d->name, properties.at(pos++));
1862 +
1863 + // process foreign fields
1864 + if (pos >= properties.size())
1865 + return;
1866 + foreach (const QByteArray &fkName, d->foreignFields.keys())
1867 + {
1868 + QObject *object = model->property(fkName + "_ptr").value<QObject*>();
1869 + if (object)
1870 + {
1871 + const QDjangoMetaModel foreignMeta = QDjango::metaModel(d->foreignFields[fkName]);
1872 + foreignMeta.load(object, properties, pos);
1873 + }
1874 + }
1875 }
1876
1877 @@ -497,8 +504,8 @@
1878 /*!
1879 - Returns the foreign field mapping.
1880 + Returns the foreign field mapping.
1881 */
1882 QMap<QByteArray, QString> QDjangoMetaModel::foreignFields() const
1883 {
1884 - return d->foreignFields;
1885 + return d->foreignFields;
1886 }
1887
1888 @@ -505,13 +512,13 @@
1889 /*!
1890 - Return the local field with the specified \a name.
1891 + Return the local field with the specified \a name.
1892 */
1893 QDjangoMetaField QDjangoMetaModel::localField(const QString &name) const
1894 {
1895 - const QString fieldName = (name == "pk") ? d->primaryKey : name;
1896 - foreach (const QDjangoMetaField &field, d->localFields) {
1897 - if (field.d->name == fieldName)
1898 - return field;
1899 - }
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)
1904 + return field;
1905 + }
1906 + return QDjangoMetaField();
1907 }
1908
1909 @@ -518,8 +525,8 @@
1910 /*!
1911 - Returns the list of local fields.
1912 + Returns the list of local fields.
1913 */
1914 QList<QDjangoMetaField> QDjangoMetaModel::localFields() const
1915 {
1916 - return d->localFields;
1917 + return d->localFields;
1918 }
1919
1920 @@ -526,8 +533,8 @@
1921 /*!
1922 - Returns the name of the primary key for the current QDjangoMetaModel.
1923 + Returns the name of the primary key for the current QDjangoMetaModel.
1924 */
1925 QByteArray QDjangoMetaModel::primaryKey() const
1926 {
1927 - return d->primaryKey;
1928 + return d->primaryKey;
1929 }
1930
1931 @@ -534,8 +541,8 @@
1932 /*!
1933 - Returns the name of the database table.
1934 + Returns the name of the database table.
1935 */
1936 QString QDjangoMetaModel::table() const
1937 {
1938 - return d->table;
1939 + return d->table;
1940 }
1941
1942 @@ -542,11 +549,11 @@
1943 /*!
1944 - Removes the given \a model instance from the database.
1945 + Removes the given \a model instance from the database.
1946 */
1947 bool QDjangoMetaModel::remove(QObject *model) const
1948 {
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();
1957 }
1958
1959 @@ -553,57 +560,57 @@
1960 /*!
1961 - Saves the given \a model instance to the database.
1962 + Saves the given \a model instance to the database.
1963
1964 - \return true if saving succeeded, false otherwise
1965 + \return true if saving succeeded, false otherwise
1966 */
1967 bool QDjangoMetaModel::save(QObject *model) const
1968 {
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()))
1973 - {
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())
1981 - {
1982 - // prepare data
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));
1988 - }
1989 - }
1990 -
1991 - // perform UPDATE
1992 - QDjangoQuerySetPrivate qs(model->metaObject()->className());
1993 - qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk));
1994 - return qs.sqlUpdate(fields) != -1;
1995 - }
1996 - }
1997 -
1998 - // prepare data
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));
2004 - }
2005 - }
2006 -
2007 - // perform INSERT
2008 - QVariant insertId;
2009 - QDjangoQuerySetPrivate qs(model->metaObject()->className());
2010 - if (!qs.sqlInsert(fields, &insertId))
2011 - return false;
2012 -
2013 - // fetch autoincrement pk
2014 - if (primaryKey.d->autoIncrement)
2015 - model->setProperty(d->primaryKey, insertId);
2016 - return true;
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()))
2021 + {
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())
2029 + {
2030 + // prepare data
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));
2036 + }
2037 + }
2038 +
2039 + // perform UPDATE
2040 + QDjangoQuerySetPrivate qs(model->metaObject()->className());
2041 + qs.addFilter(QDjangoWhere("pk", QDjangoWhere::Equals, pk));
2042 + return qs.sqlUpdate(fields) != -1;
2043 + }
2044 + }
2045 +
2046 + // prepare data
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));
2052 + }
2053 + }
2054 +
2055 + // perform INSERT
2056 + QVariant insertId;
2057 + QDjangoQuerySetPrivate qs(model->metaObject()->className());
2058 + if (!qs.sqlInsert(fields, &insertId))
2059 + return false;
2060 +
2061 + // fetch autoincrement pk
2062 + if (primaryKey.d->autoIncrement)
2063 + model->setProperty(d->primaryKey, insertId);
2064 + return true;
2065 }
2066 """]]
2067
2068 ## The extracted whitespace changes
2069
2070 [[!format diff """
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;
2083 + }
2084 d->localFields << field;
2085 continue;
2086 }
2087 """]]
This page took 0.143633 seconds and 5 git commands to generate.