~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/sql/models/qsqlrelationaltablemodel.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the sql module of the Qt Toolkit.
 
6
**
 
7
** This file may be distributed under the terms of the Q Public License
 
8
** as defined by Trolltech AS of Norway and appearing in the file
 
9
** LICENSE.QPL included in the packaging of this file.
 
10
**
 
11
** This file may be distributed and/or modified under the terms of the
 
12
** GNU General Public License version 2 as published by the Free Software
 
13
** Foundation and appearing in the file LICENSE.GPL included in the
 
14
** packaging of this file.
 
15
**
 
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
17
**   information about Qt Commercial License Agreements.
 
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
#include "qsqlrelationaltablemodel.h"
 
30
 
 
31
#include "qhash.h"
 
32
#include "qstringlist.h"
 
33
#include "qsqldatabase.h"
 
34
#include "qsqlerror.h"
 
35
#include "qsqlfield.h"
 
36
#include "qsqlindex.h"
 
37
#include "qsqlquery.h"
 
38
#include "qsqlrecord.h"
 
39
 
 
40
#include "qsqltablemodel_p.h"
 
41
 
 
42
/*!
 
43
    \class QSqlRelation
 
44
    \brief The QSqlRelation class stores information about an SQL foreign key.
 
45
 
 
46
    QSqlRelation is a helper class for QSqlRelationalTableModel. See
 
47
    QSqlRelationalTableModel::setRelation() and
 
48
    QSqlRelationalTableModel::relation() for details.
 
49
 
 
50
    \sa QSqlRelationalTableModel, QSqlRelationalDelegate
 
51
*/
 
52
 
 
53
/*!
 
54
    \fn QSqlRelation::QSqlRelation()
 
55
 
 
56
    Constructs an invalid QSqlRelation object.
 
57
 
 
58
    For such an object, the tableName(), indexColumn(), and
 
59
    displayColumn() functions return an empty string.
 
60
 
 
61
    \sa isValid()
 
62
*/
 
63
 
 
64
/*!
 
65
    \fn QSqlRelation::QSqlRelation(const QString &tableName, const QString &indexColumn,
 
66
                                   const QString &displayColumn)
 
67
 
 
68
    Constructs a QSqlRelation object, where \a tableName is the SQL
 
69
    table name to which a foreign key refers, \a indexColumn is the
 
70
    foreign key, and \a displayColumn is the field that should be
 
71
    presented to the user.
 
72
 
 
73
    \sa tableName(), indexColumn(), displayColumn()
 
74
*/
 
75
 
 
76
/*!
 
77
    \fn QString QSqlRelation::tableName() const
 
78
 
 
79
    Returns the name of the table to which a foreign key refers.
 
80
*/
 
81
 
 
82
/*!
 
83
    \fn QString QSqlRelation::indexColumn() const
 
84
 
 
85
    Returns the index column from table tableName() to which a
 
86
    foreign key refers.
 
87
*/
 
88
 
 
89
/*!
 
90
    \fn QString QSqlRelation::displayColumn() const
 
91
 
 
92
    Returns the column from table tableName() that should be
 
93
    presented to the user instead of a foreign key.
 
94
*/
 
95
 
 
96
/*!
 
97
    \fn bool QSqlRelation::isValid() const
 
98
 
 
99
    Returns true if the QSqlRelation object is valid; otherwise
 
100
    returns false.
 
101
*/
 
102
 
 
103
struct Relation
 
104
{
 
105
    Relation(): model(0) {}
 
106
    QSqlRelation rel;
 
107
    QSqlTableModel *model;
 
108
    QHash<int, QVariant> displayValues;
 
109
};
 
110
 
 
111
class QSqlRelationalTableModelPrivate: public QSqlTableModelPrivate
 
112
{
 
113
public:
 
114
    QSqlRelationalTableModelPrivate()
 
115
        : QSqlTableModelPrivate()
 
116
    {}
 
117
 
 
118
    mutable QVector<Relation> relations;
 
119
    QSqlRecord baseRec; // the record without relations
 
120
    void clearChanges();
 
121
};
 
122
 
 
123
static void qAppendWhereClause(QString &query, const QString &clause1, const QString &clause2)
 
124
{
 
125
    if (clause1.isEmpty() && clause2.isEmpty())
 
126
        return;
 
127
    if (clause1.isEmpty() || clause2.isEmpty())
 
128
        query.append(QLatin1String(" WHERE (")).append(clause1).append(clause2);
 
129
    else
 
130
        query.append(QLatin1String(" WHERE (")).append(clause1).append(
 
131
                        QLatin1String(") AND (")).append(clause2);
 
132
    query.append(QLatin1String(") "));
 
133
}
 
134
 
 
135
void QSqlRelationalTableModelPrivate::clearChanges()
 
136
{
 
137
    for (int i = 0; i < relations.count(); ++i) {
 
138
        Relation &rel = relations[i];
 
139
        delete rel.model;
 
140
        rel.displayValues.clear();
 
141
    }
 
142
}
 
143
 
 
144
/*!
 
145
    \class QSqlRelationalTableModel
 
146
    \brief The QSqlRelationalTableModel class provides an editable
 
147
    data model for a single database table, with foreign key support.
 
148
 
 
149
    \ingroup database
 
150
    \module sql
 
151
 
 
152
    QSqlRelationalTableModel acts like QSqlTableModel, but allows
 
153
    columns to be set as foreign keys into other database tables.
 
154
 
 
155
    \table
 
156
    \row \o \inlineimage noforeignkeys.png
 
157
         \o \inlineimage foreignkeys.png
 
158
    \endtable
 
159
 
 
160
    The screenshot on the left shows a plain QSqlTableModel in a
 
161
    QTableView. Foreign keys (\c city and \c country) aren't resolved
 
162
    to human-readable values. The screenshot on the right shows a
 
163
    QSqlRelationalTableModel, with foreign keys resolved into
 
164
    human-readable text strings.
 
165
 
 
166
    The following code snippet shows how the QSqlRelationalTableModel
 
167
    was set up:
 
168
 
 
169
    \quotefromfile sql/relationaltablemodel/relationaltablemodel.cpp
 
170
    \skipto model->setTable
 
171
    \printline model->setTable
 
172
    \skipto setRelation
 
173
    \printline setRelation
 
174
    \printline setRelation
 
175
 
 
176
    The setRelation() function calls establish a relationship between
 
177
    two tables. The first call specifies that column 2 in table \c
 
178
    employee is a foreign key that maps with field \c id of table \c
 
179
    city, and that the view should present the \c{city}'s \c name
 
180
    field to the user. The second call does something similar with
 
181
    column 3.
 
182
 
 
183
    If you use a read-write QSqlRelationalTableModel, you probably
 
184
    want to use QSqlRelatinalDelegate on the view. Unlike the default
 
185
    delegate, QSqlRelationalDelegate provides a combobox for fields
 
186
    that are foreign keys into other tables. To use the class, simply
 
187
    call QAbstractItemView::setItemDelegate() on the view with an
 
188
    instance of QSqlRelationalDelegate:
 
189
 
 
190
    \quotefromfile sql/relationaltablemodel/relationaltablemodel.cpp
 
191
    \skipto QTableView *view = new
 
192
    \printuntil setItemDelegate
 
193
 
 
194
    The \l{sql/relationaltablemodel} example illustrates how to use
 
195
    QSqlRelationalTableModel in conjunction with
 
196
    QSqlRelationalDelegate to provide tables with foreigh key
 
197
    support.
 
198
 
 
199
    \image relationaltable.png
 
200
 
 
201
    Note: The table's primary key may not contain a relation to
 
202
    another table.
 
203
 
 
204
    \sa QSqlRelation, QSqlRelationalDelegate
 
205
*/
 
206
 
 
207
 
 
208
/*!
 
209
    Creates an empty QSqlRelationalTableModel and sets the parent to \a parent
 
210
    and the database connection to \a db. If \a db is not valid, the
 
211
    default database connection will be used.
 
212
*/
 
213
QSqlRelationalTableModel::QSqlRelationalTableModel(QObject *parent, QSqlDatabase db)
 
214
    : QSqlTableModel(*new QSqlRelationalTableModelPrivate, parent, db)
 
215
{
 
216
}
 
217
 
 
218
/*!
 
219
    Destroys the object and frees any allocated resources.
 
220
*/
 
221
QSqlRelationalTableModel::~QSqlRelationalTableModel()
 
222
{
 
223
}
 
224
 
 
225
/*!
 
226
    \reimp
 
227
*/
 
228
QVariant QSqlRelationalTableModel::data(const QModelIndex &index, int role) const
 
229
{
 
230
    Q_D(const QSqlRelationalTableModel);
 
231
    if (role == Qt::DisplayRole && index.column() > 0 && index.column() < d->relations.count()) {
 
232
        const QVariant v = d->relations.at(index.column()).displayValues.value(index.row());
 
233
        if (v.isValid())
 
234
            return v;
 
235
    }
 
236
 
 
237
    return QSqlTableModel::data(index, role);
 
238
}
 
239
 
 
240
/*!
 
241
    Sets the data for the \a role in the item with the specified \a
 
242
    index to the \a value given. Depending on the edit strategy, the
 
243
    value might be applied to the database at once, or it may be
 
244
    cached in the model.
 
245
 
 
246
    Returns true if the value could be set, or false on error (for
 
247
    example, if \a index is out of bounds).
 
248
 
 
249
    For relational columns, \a value must be the index, not the
 
250
    display value.
 
251
 
 
252
    \sa editStrategy(), data(), submit(), revertRow()
 
253
*/
 
254
bool QSqlRelationalTableModel::setData(const QModelIndex &index, const QVariant &value,
 
255
                                       int role)
 
256
{
 
257
    Q_D(QSqlRelationalTableModel);
 
258
    if (role == Qt::DisplayRole && index.column() > 0 && index.column() < d->relations.count()) {
 
259
        d->relations[index.column()].displayValues[index.row()] = value;
 
260
        return true;
 
261
    }
 
262
 
 
263
    return QSqlTableModel::setData(index, value, role);
 
264
}
 
265
 
 
266
/*!
 
267
    Lets the specified \a column be a foreign index specified by \a relation.
 
268
 
 
269
    Example:
 
270
 
 
271
    \quotefromfile sql/relationaltablemodel/relationaltablemodel.cpp
 
272
    \skipto model->setTable
 
273
    \printline model->setTable
 
274
    \skipto setRelation
 
275
    \printline setRelation
 
276
 
 
277
    The setRelation() call specifies that column 2 in table \c
 
278
    employee is a foreign key that maps with field \c id of table \c
 
279
    city, and that the view should present the \c{city}'s \c name
 
280
    field to the user.
 
281
 
 
282
    \sa relation()
 
283
*/
 
284
void QSqlRelationalTableModel::setRelation(int column, const QSqlRelation &relation)
 
285
{
 
286
    Q_D(QSqlRelationalTableModel);
 
287
    if (column < 0)
 
288
        return;
 
289
    if (d->relations.size() <= column)
 
290
        d->relations.resize(column + 1);
 
291
    d->relations[column].rel = relation;
 
292
}
 
293
 
 
294
/*!
 
295
    Returns the relation for the column \a column, or an invalid
 
296
    relation if no relation is set.
 
297
 
 
298
    \sa setRelation(), QSqlRelation::isValid()
 
299
*/
 
300
QSqlRelation QSqlRelationalTableModel::relation(int column) const
 
301
{
 
302
    Q_D(const QSqlRelationalTableModel);
 
303
    return d->relations.value(column).rel;
 
304
}
 
305
 
 
306
/*!
 
307
    \reimp
 
308
*/
 
309
QString QSqlRelationalTableModel::selectStatement() const
 
310
{
 
311
    Q_D(const QSqlRelationalTableModel);
 
312
    QString query;
 
313
 
 
314
    if (tableName().isEmpty())
 
315
        return query;
 
316
    if (d->relations.isEmpty())
 
317
        return QSqlTableModel::selectStatement();
 
318
 
 
319
    QString tList;
 
320
    QString fList;
 
321
    QString where;
 
322
 
 
323
    QSqlRecord rec = database().record(tableName());
 
324
    QStringList tables;
 
325
    const Relation nullRelation;
 
326
    for (int i = 0; i < rec.count(); ++i) {
 
327
        QSqlRelation relation = d->relations.value(i, nullRelation).rel;
 
328
        if (relation.isValid()) {
 
329
            fList.append(relation.tableName()).append(QLatin1Char('.'));
 
330
            fList.append(relation.displayColumn()).append(QLatin1Char(','));
 
331
            if (!tables.contains(relation.tableName()))
 
332
                tables.append(relation.tableName());
 
333
            where.append(tableName()).append(QLatin1Char('.')).append(rec.fieldName(i));
 
334
            where.append(QLatin1Char('=')).append(relation.tableName()).append(QLatin1Char('.'));
 
335
            where.append(relation.indexColumn()).append(QLatin1String(" and "));
 
336
        } else {
 
337
            fList.append(tableName()).append(QLatin1Char('.')).append(rec.fieldName(i)).append(
 
338
                            QLatin1Char(','));
 
339
        }
 
340
    }
 
341
    if (!tables.isEmpty())
 
342
        tList.append(tables.join(QLatin1String(","))).append(QLatin1String(","));
 
343
    if (fList.isEmpty())
 
344
        return query;
 
345
    tList.prepend(QLatin1Char(',')).prepend(tableName());
 
346
    // truncate tailing comma
 
347
    tList.chop(1);
 
348
    fList.chop(1);
 
349
    query.append(QLatin1String("SELECT "));
 
350
    query.append(fList).append(QLatin1String(" FROM ")).append(tList);
 
351
    if (!where.isEmpty())
 
352
        where.chop(5);
 
353
    qAppendWhereClause(query, where, filter());
 
354
 
 
355
    QString orderBy = orderByClause();
 
356
    if (!orderBy.isEmpty())
 
357
        query.append(QLatin1Char(' ')).append(orderBy);
 
358
 
 
359
    return query;
 
360
}
 
361
 
 
362
/*!
 
363
    Returns a QSqlTableModel object for accessing the table for which
 
364
    \a column is a foreign key, or 0 if there is no relation for the
 
365
    given \a column.
 
366
 
 
367
    The returned object is owned by the QSqlRelationalTableModel.
 
368
 
 
369
    \sa setRelation(), relation()
 
370
*/
 
371
QSqlTableModel *QSqlRelationalTableModel::relationModel(int column) const
 
372
{
 
373
    Q_D(const QSqlRelationalTableModel);
 
374
    Relation relation = d->relations.value(column);
 
375
    if (!relation.rel.isValid())
 
376
        return 0;
 
377
 
 
378
    QSqlTableModel *childModel = relation.model;
 
379
    if (!childModel) {
 
380
        childModel = new QSqlTableModel(const_cast<QSqlRelationalTableModel *>(this), database());
 
381
        childModel->setTable(relation.rel.tableName());
 
382
        childModel->select();
 
383
        d->relations[column].model = childModel;
 
384
    }
 
385
    return childModel;
 
386
}
 
387
 
 
388
/*!
 
389
    \reimp
 
390
*/
 
391
void QSqlRelationalTableModel::revertRow(int row)
 
392
{
 
393
    Q_D(QSqlRelationalTableModel);
 
394
    for (int i = 0; i < d->relations.count(); ++i)
 
395
        d->relations[i].displayValues.remove(row);
 
396
    QSqlTableModel::revertRow(row);
 
397
}
 
398
 
 
399
/*!
 
400
    \reimp
 
401
*/
 
402
void QSqlRelationalTableModel::clear()
 
403
{
 
404
    Q_D(QSqlRelationalTableModel);
 
405
    d->clearChanges();
 
406
    d->relations.clear();
 
407
    QSqlTableModel::clear();
 
408
}
 
409
 
 
410
/*!
 
411
    \reimp
 
412
*/
 
413
bool QSqlRelationalTableModel::select()
 
414
{
 
415
    return QSqlTableModel::select();
 
416
}
 
417
 
 
418
/*!
 
419
    \reimp
 
420
*/
 
421
void QSqlRelationalTableModel::setTable(const QString &table)
 
422
{
 
423
    Q_D(QSqlRelationalTableModel);
 
424
 
 
425
    // memorize the table before applying the relations
 
426
    d->baseRec = d->db.record(table);
 
427
 
 
428
    QSqlTableModel::setTable(table);
 
429
}
 
430
 
 
431
/*!
 
432
    \reimp
 
433
*/
 
434
bool QSqlRelationalTableModel::updateRowInTable(int row, const QSqlRecord &values)
 
435
{
 
436
    Q_D(QSqlRelationalTableModel);
 
437
 
 
438
    QSqlRecord rec = values;
 
439
 
 
440
    // translate the field names
 
441
    for (int i = 0; i < values.count(); ++i) {
 
442
        int realCol = indexInQuery(createIndex(row, i)).column();
 
443
        if (realCol != -1 && d->relations.value(realCol).rel.isValid()) {
 
444
            QVariant v = values.value(i);
 
445
            rec.replace(i, d->baseRec.field(realCol));
 
446
            rec.setValue(i, v);
 
447
        }
 
448
    }
 
449
    return QSqlTableModel::updateRowInTable(row, rec);
 
450
}
 
451
 
 
452
/*!
 
453
    \reimp
 
454
*/
 
455
QString QSqlRelationalTableModel::orderByClause() const
 
456
{
 
457
    Q_D(const QSqlRelationalTableModel);
 
458
 
 
459
    const QSqlRelation rel = d->relations.value(d->sortColumn).rel;
 
460
    if (!rel.isValid())
 
461
        return QSqlTableModel::orderByClause();
 
462
 
 
463
    QString s = QLatin1String("ORDER BY ");
 
464
    s.append(rel.tableName()).append(QLatin1Char('.')).append(d->rec.field(d->sortColumn).name());
 
465
    s += d->sortOrder == Qt::AscendingOrder ? QLatin1String(" ASC") : QLatin1String(" DESC");
 
466
    return s;
 
467
}
 
468