blob: 93e005f9ee65598a956a8d07ccb02c9161d0a1a4 [file] [log] [blame]
showard35444862008-08-07 22:35:30 +00001from django.db import models as dbmodels, connection
2from django.utils import datastructures
3from autotest_lib.frontend.afe import model_logic, readonly_connection
4
5class TempManager(model_logic.ExtendedManager):
6 _GROUP_COUNT_NAME = 'group_count'
7
8 def _get_key_unless_is_function(self, field):
9 if '(' in field:
10 return field
showard7c199df2008-10-03 10:17:15 +000011 return self.get_key_on_this_table(field)
showard35444862008-08-07 22:35:30 +000012
13
showardf2489522008-10-23 23:08:00 +000014 def _get_field_names(self, fields, extra_select_fields={}):
15 field_names = []
16 for field in fields:
17 if field in extra_select_fields:
18 field_names.append(field)
19 else:
20 field_names.append(self._get_key_unless_is_function(field))
21 return field_names
showard35444862008-08-07 22:35:30 +000022
23
24 def _get_group_query_sql(self, query, group_by, extra_select_fields):
showardf2489522008-10-23 23:08:00 +000025 group_fields = self._get_field_names(group_by, extra_select_fields)
showard35444862008-08-07 22:35:30 +000026
showardf2489522008-10-23 23:08:00 +000027 select_fields = [field for field in group_fields
28 if field not in extra_select_fields]
showard7c199df2008-10-03 10:17:15 +000029 for field_name, field_sql in extra_select_fields.iteritems():
30 field_sql = self._get_key_unless_is_function(field_sql)
31 select_fields.append(field_sql + ' AS ' + field_name)
32 # add the extra fields to the query selects, so they'll be sortable
33 # and Django won't mess with any of them
showard8a6eb0c2008-10-01 11:38:59 +000034 query._select[field_name] = field_sql
showard35444862008-08-07 22:35:30 +000035
showard35444862008-08-07 22:35:30 +000036 _, where, params = query._get_sql_clause()
showardd50ffb42008-09-04 02:47:45 +000037
38 # insert GROUP BY clause into query
showard7c199df2008-10-03 10:17:15 +000039 group_by_clause = ' GROUP BY ' + ', '.join(group_fields)
showardd50ffb42008-09-04 02:47:45 +000040 group_by_position = where.rfind('ORDER BY')
41 if group_by_position == -1:
42 group_by_position = len(where)
43 where = (where[:group_by_position] +
44 group_by_clause + ' ' +
45 where[group_by_position:])
showard35444862008-08-07 22:35:30 +000046
47 return ('SELECT ' + ', '.join(select_fields) + where), params
48
49
showard06b82fc2009-06-30 01:59:42 +000050 def _get_column_names(self, cursor):
51 """\
52 Gets the column names from the cursor description. This method exists
53 so that it can be mocked in the unit test for sqlite3 compatibility."
54 """
55 return [column_info[0] for column_info in cursor.description]
56
57
showard8a6eb0c2008-10-01 11:38:59 +000058 def execute_group_query(self, query, group_by, extra_select_fields=[]):
showard35444862008-08-07 22:35:30 +000059 """
showard8a6eb0c2008-10-01 11:38:59 +000060 Performs the given query grouped by the fields in group_by with the
showard7c199df2008-10-03 10:17:15 +000061 given extra select fields added. extra_select_fields should be a dict
62 mapping field alias to field SQL. Usually, the extra fields will use
showard8a6eb0c2008-10-01 11:38:59 +000063 group aggregation functions. Returns a list of dicts, where each dict
64 corresponds to single row and contains a key for each grouped field as
65 well as all of the extra select fields.
showard35444862008-08-07 22:35:30 +000066 """
67 sql, params = self._get_group_query_sql(query, group_by,
68 extra_select_fields)
showard56e93772008-10-06 10:06:22 +000069 cursor = readonly_connection.connection().cursor()
showard8a6eb0c2008-10-01 11:38:59 +000070 cursor.execute(sql, params)
showard06b82fc2009-06-30 01:59:42 +000071 field_names = self._get_column_names(cursor)
showard8a6eb0c2008-10-01 11:38:59 +000072 row_dicts = [dict(zip(field_names, row)) for row in cursor.fetchall()]
73 return row_dicts
74
75
76 def get_count_sql(self, query):
77 """
78 Get the SQL to properly select a per-group count of unique matches for
showard7c199df2008-10-03 10:17:15 +000079 a grouped query. Returns a tuple (field alias, field SQL)
showard8a6eb0c2008-10-01 11:38:59 +000080 """
81 if query._distinct:
showard7c199df2008-10-03 10:17:15 +000082 pk_field = self.get_key_on_this_table()
showard8a6eb0c2008-10-01 11:38:59 +000083 count_sql = 'COUNT(DISTINCT %s)' % pk_field
84 else:
85 count_sql = 'COUNT(1)'
showard7c199df2008-10-03 10:17:15 +000086 return self._GROUP_COUNT_NAME, count_sql
showard35444862008-08-07 22:35:30 +000087
88
89 def _get_num_groups_sql(self, query, group_by):
90 group_fields = self._get_field_names(group_by)
showard8a6eb0c2008-10-01 11:38:59 +000091 query._order_by = None # this can mess up the query and isn't needed
showard35444862008-08-07 22:35:30 +000092 _, where, params = query._get_sql_clause()
93 return ('SELECT COUNT(DISTINCT %s) %s' % (','.join(group_fields),
94 where),
95 params)
96
97
98 def get_num_groups(self, query, group_by):
99 """
100 Returns the number of distinct groups for the given query grouped by the
101 fields in group_by.
102 """
103 sql, params = self._get_num_groups_sql(query, group_by)
showard56e93772008-10-06 10:06:22 +0000104 cursor = readonly_connection.connection().cursor()
showard35444862008-08-07 22:35:30 +0000105 cursor.execute(sql, params)
106 return cursor.fetchone()[0]
107
108
109class Machine(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000110 machine_idx = dbmodels.AutoField(primary_key=True)
showard35444862008-08-07 22:35:30 +0000111 hostname = dbmodels.CharField(unique=True, maxlength=300)
112 machine_group = dbmodels.CharField(blank=True, maxlength=240)
113 owner = dbmodels.CharField(blank=True, maxlength=240)
114
115 class Meta:
116 db_table = 'machines'
117
118
119class Kernel(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000120 kernel_idx = dbmodels.AutoField(primary_key=True)
showard35444862008-08-07 22:35:30 +0000121 kernel_hash = dbmodels.CharField(maxlength=105, editable=False)
122 base = dbmodels.CharField(maxlength=90)
123 printable = dbmodels.CharField(maxlength=300)
124
125 class Meta:
126 db_table = 'kernels'
127
128
129class Patch(dbmodels.Model):
130 kernel = dbmodels.ForeignKey(Kernel, db_column='kernel_idx')
131 name = dbmodels.CharField(blank=True, maxlength=240)
132 url = dbmodels.CharField(blank=True, maxlength=900)
133 hash_ = dbmodels.CharField(blank=True, maxlength=105, db_column='hash')
134
135 class Meta:
136 db_table = 'patches'
137
138
139class Status(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000140 status_idx = dbmodels.AutoField(primary_key=True)
showard35444862008-08-07 22:35:30 +0000141 word = dbmodels.CharField(maxlength=30)
142
143 class Meta:
144 db_table = 'status'
145
146
147class Job(dbmodels.Model):
showardf8b19042009-05-12 17:22:49 +0000148 job_idx = dbmodels.AutoField(primary_key=True)
showard35444862008-08-07 22:35:30 +0000149 tag = dbmodels.CharField(unique=True, maxlength=300)
150 label = dbmodels.CharField(maxlength=300)
151 username = dbmodels.CharField(maxlength=240)
152 machine = dbmodels.ForeignKey(Machine, db_column='machine_idx')
153 queued_time = dbmodels.DateTimeField(null=True, blank=True)
154 started_time = dbmodels.DateTimeField(null=True, blank=True)
155 finished_time = dbmodels.DateTimeField(null=True, blank=True)
156
157 class Meta:
158 db_table = 'jobs'
159
160
showardf8b19042009-05-12 17:22:49 +0000161class Test(dbmodels.Model, model_logic.ModelExtensions,
162 model_logic.ModelWithAttributes):
163 test_idx = dbmodels.AutoField(primary_key=True)
showard35444862008-08-07 22:35:30 +0000164 job = dbmodels.ForeignKey(Job, db_column='job_idx')
165 test = dbmodels.CharField(maxlength=90)
166 subdir = dbmodels.CharField(blank=True, maxlength=180)
167 kernel = dbmodels.ForeignKey(Kernel, db_column='kernel_idx')
168 status = dbmodels.ForeignKey(Status, db_column='status')
169 reason = dbmodels.CharField(blank=True, maxlength=3072)
170 machine = dbmodels.ForeignKey(Machine, db_column='machine_idx')
171 finished_time = dbmodels.DateTimeField(null=True, blank=True)
172 started_time = dbmodels.DateTimeField(null=True, blank=True)
173
showardf8b19042009-05-12 17:22:49 +0000174 objects = model_logic.ExtendedManager()
175
176 def _get_attribute_model_and_args(self, attribute):
177 return TestAttribute, dict(test=self, attribute=attribute,
178 user_created=True)
179
180
181 def set_attribute(self, attribute, value):
182 # ensure non-user-created attributes remain immutable
183 try:
184 TestAttribute.objects.get(test=self, attribute=attribute,
185 user_created=False)
186 raise ValueError('Attribute %s already exists for test %s and is '
187 'immutable' % (attribute, self.test_idx))
188 except TestAttribute.DoesNotExist:
189 super(Test, self).set_attribute(attribute, value)
190
191
showard35444862008-08-07 22:35:30 +0000192 class Meta:
193 db_table = 'tests'
194
195
showarde732ee72008-09-23 19:15:43 +0000196class TestAttribute(dbmodels.Model, model_logic.ModelExtensions):
showardf8b19042009-05-12 17:22:49 +0000197 test = dbmodels.ForeignKey(Test, db_column='test_idx')
showard35444862008-08-07 22:35:30 +0000198 attribute = dbmodels.CharField(maxlength=90)
199 value = dbmodels.CharField(blank=True, maxlength=300)
showardf8b19042009-05-12 17:22:49 +0000200 user_created = dbmodels.BooleanField(default=False)
201
202 objects = model_logic.ExtendedManager()
showard35444862008-08-07 22:35:30 +0000203
204 class Meta:
205 db_table = 'test_attributes'
206
207
jadmanski430dca92008-12-16 20:56:53 +0000208class IterationAttribute(dbmodels.Model, model_logic.ModelExtensions):
showardf8b19042009-05-12 17:22:49 +0000209 # this isn't really a primary key, but it's necessary to appease Django
210 # and is harmless as long as we're careful
showarde732ee72008-09-23 19:15:43 +0000211 test = dbmodels.ForeignKey(Test, db_column='test_idx', primary_key=True)
showard35444862008-08-07 22:35:30 +0000212 iteration = dbmodels.IntegerField()
213 attribute = dbmodels.CharField(maxlength=90)
214 value = dbmodels.CharField(blank=True, maxlength=300)
215
showardf8b19042009-05-12 17:22:49 +0000216 objects = model_logic.ExtendedManager()
217
showard35444862008-08-07 22:35:30 +0000218 class Meta:
219 db_table = 'iteration_attributes'
220
221
jadmanski430dca92008-12-16 20:56:53 +0000222class IterationResult(dbmodels.Model, model_logic.ModelExtensions):
showardf8b19042009-05-12 17:22:49 +0000223 # see comment on IterationAttribute regarding primary_key=True
jadmanski430dca92008-12-16 20:56:53 +0000224 test = dbmodels.ForeignKey(Test, db_column='test_idx', primary_key=True)
showard35444862008-08-07 22:35:30 +0000225 iteration = dbmodels.IntegerField()
226 attribute = dbmodels.CharField(maxlength=90)
227 value = dbmodels.FloatField(null=True, max_digits=12, decimal_places=31,
228 blank=True)
229
showardf8b19042009-05-12 17:22:49 +0000230 objects = model_logic.ExtendedManager()
231
showard35444862008-08-07 22:35:30 +0000232 class Meta:
233 db_table = 'iteration_result'
234
235
236class TestLabel(dbmodels.Model, model_logic.ModelExtensions):
showardd50ffb42008-09-04 02:47:45 +0000237 name = dbmodels.CharField(maxlength=80, unique=True)
showard35444862008-08-07 22:35:30 +0000238 description = dbmodels.TextField(blank=True)
239 tests = dbmodels.ManyToManyField(Test, blank=True,
240 filter_interface=dbmodels.HORIZONTAL)
241
242 name_field = 'name'
showardf8b19042009-05-12 17:22:49 +0000243 objects = model_logic.ExtendedManager()
showard35444862008-08-07 22:35:30 +0000244
245 class Meta:
246 db_table = 'test_labels'
247
248
249class SavedQuery(dbmodels.Model, model_logic.ModelExtensions):
250 # TODO: change this to foreign key once DBs are merged
251 owner = dbmodels.CharField(maxlength=80)
252 name = dbmodels.CharField(maxlength=100)
253 url_token = dbmodels.TextField()
254
255 class Meta:
256 db_table = 'saved_queries'
257
258
showardce12f552008-09-19 00:48:59 +0000259class EmbeddedGraphingQuery(dbmodels.Model, model_logic.ModelExtensions):
260 url_token = dbmodels.TextField(null=False, blank=False)
261 graph_type = dbmodels.CharField(maxlength=16, null=False, blank=False)
262 params = dbmodels.TextField(null=False, blank=False)
263 last_updated = dbmodels.DateTimeField(null=False, blank=False,
264 editable=False)
265 # refresh_time shows the time at which a thread is updating the cached
266 # image, or NULL if no one is updating the image. This is used so that only
267 # one thread is updating the cached image at a time (see
268 # graphing_utils.handle_plot_request)
269 refresh_time = dbmodels.DateTimeField(editable=False)
270 cached_png = dbmodels.TextField(editable=False)
271
272 class Meta:
273 db_table = 'embedded_graphing_queries'
274
275
showard35444862008-08-07 22:35:30 +0000276# views
277
278class TestViewManager(TempManager):
showard35444862008-08-07 22:35:30 +0000279 def get_query_set(self):
280 query = super(TestViewManager, self).get_query_set()
showard5bf7c502008-08-20 01:22:22 +0000281
showard35444862008-08-07 22:35:30 +0000282 # add extra fields to selects, using the SQL itself as the "alias"
283 extra_select = dict((sql, sql)
284 for sql in self.model.extra_fields.iterkeys())
285 return query.extra(select=extra_select)
286
287
showardf2489522008-10-23 23:08:00 +0000288 def _get_include_exclude_suffix(self, exclude):
289 if exclude:
290 suffix = '_exclude'
291 else:
292 suffix = '_include'
293 return suffix
294
295
showard64aeecd2008-09-19 21:32:58 +0000296 def _add_label_joins(self, query_set, suffix=''):
showard43a3d262008-11-12 18:17:05 +0000297 query_set = self.add_join(query_set, 'test_labels_tests',
showard64aeecd2008-09-19 21:32:58 +0000298 join_key='test_id', suffix=suffix,
299 force_left_join=True)
showardd50ffb42008-09-04 02:47:45 +0000300
301 second_join_alias = 'test_labels' + suffix
302 second_join_condition = ('%s.id = %s.testlabel_id' %
showard64aeecd2008-09-19 21:32:58 +0000303 (second_join_alias,
304 'test_labels_tests' + suffix))
305 filter_object = self._CustomSqlQ()
showardd50ffb42008-09-04 02:47:45 +0000306 filter_object.add_join('test_labels',
307 second_join_condition,
308 'LEFT JOIN',
309 alias=second_join_alias)
showard64aeecd2008-09-19 21:32:58 +0000310 return query_set.filter(filter_object)
showardd50ffb42008-09-04 02:47:45 +0000311
showard64aeecd2008-09-19 21:32:58 +0000312
showardf2489522008-10-23 23:08:00 +0000313 def _add_attribute_join(self, query_set, join_condition='', suffix=None,
showard64aeecd2008-09-19 21:32:58 +0000314 exclude=False):
showard93caf2d2009-05-08 18:24:22 +0000315 join_condition = self.escape_user_sql(join_condition)
showardf2489522008-10-23 23:08:00 +0000316 if suffix is None:
317 suffix = self._get_include_exclude_suffix(exclude)
showard43a3d262008-11-12 18:17:05 +0000318 return self.add_join(query_set, 'test_attributes',
319 join_key='test_idx',
showard64aeecd2008-09-19 21:32:58 +0000320 join_condition=join_condition,
321 suffix=suffix, exclude=exclude)
showardd50ffb42008-09-04 02:47:45 +0000322
323
324 def _get_label_ids_from_names(self, label_names):
showard64aeecd2008-09-19 21:32:58 +0000325 if not label_names:
326 return []
showardd50ffb42008-09-04 02:47:45 +0000327 query = TestLabel.objects.filter(name__in=label_names).values('id')
showardfc8c6ae2008-11-11 19:06:01 +0000328 return [str(label['id']) for label in query]
showard02813502008-08-20 20:52:56 +0000329
330
showardf2489522008-10-23 23:08:00 +0000331 def get_query_set_with_joins(self, filter_data, include_host_labels=False):
showardfc8c6ae2008-11-11 19:06:01 +0000332 include_labels = filter_data.pop('include_labels', [])
showardd50ffb42008-09-04 02:47:45 +0000333 exclude_labels = filter_data.pop('exclude_labels', [])
showard35444862008-08-07 22:35:30 +0000334 query_set = self.get_query_set()
showardd50ffb42008-09-04 02:47:45 +0000335 joined = False
showard35444862008-08-07 22:35:30 +0000336 # TODO: make this check more thorough if necessary
showardf2489522008-10-23 23:08:00 +0000337 extra_where = filter_data.get('extra_where', '')
338 if 'test_labels' in extra_where:
showard02813502008-08-20 20:52:56 +0000339 query_set = self._add_label_joins(query_set)
showardd50ffb42008-09-04 02:47:45 +0000340 joined = True
341
showardfc8c6ae2008-11-11 19:06:01 +0000342 include_label_ids = self._get_label_ids_from_names(include_labels)
343 if include_label_ids:
344 # TODO: Factor this out like what's done with attributes
345 condition = ('test_labels_tests_include.testlabel_id IN (%s)' %
346 ','.join(include_label_ids))
showard43a3d262008-11-12 18:17:05 +0000347 query_set = self.add_join(query_set, 'test_labels_tests',
showardfc8c6ae2008-11-11 19:06:01 +0000348 join_key='test_id',
349 suffix='_include',
350 join_condition=condition)
351 joined = True
352
showard64aeecd2008-09-19 21:32:58 +0000353 exclude_label_ids = self._get_label_ids_from_names(exclude_labels)
354 if exclude_label_ids:
355 condition = ('test_labels_tests_exclude.testlabel_id IN (%s)' %
showardfc8c6ae2008-11-11 19:06:01 +0000356 ','.join(exclude_label_ids))
showard43a3d262008-11-12 18:17:05 +0000357 query_set = self.add_join(query_set, 'test_labels_tests',
showard64aeecd2008-09-19 21:32:58 +0000358 join_key='test_id',
359 suffix='_exclude',
360 join_condition=condition,
361 exclude=True)
362 joined = True
363
364 include_attributes_where = filter_data.pop('include_attributes_where',
365 '')
366 exclude_attributes_where = filter_data.pop('exclude_attributes_where',
367 '')
368 if include_attributes_where:
369 query_set = self._add_attribute_join(
showardf2489522008-10-23 23:08:00 +0000370 query_set, join_condition=include_attributes_where)
showard64aeecd2008-09-19 21:32:58 +0000371 joined = True
372 if exclude_attributes_where:
373 query_set = self._add_attribute_join(
showardf2489522008-10-23 23:08:00 +0000374 query_set, join_condition=exclude_attributes_where,
showard64aeecd2008-09-19 21:32:58 +0000375 exclude=True)
376 joined = True
showardd50ffb42008-09-04 02:47:45 +0000377
378 if not joined:
showard35444862008-08-07 22:35:30 +0000379 filter_data['no_distinct'] = True
showardd50ffb42008-09-04 02:47:45 +0000380
showardf2489522008-10-23 23:08:00 +0000381 if include_host_labels or 'test_attributes_host_labels' in extra_where:
382 query_set = self._add_attribute_join(
383 query_set, suffix='_host_labels',
384 join_condition='test_attributes_host_labels.attribute = '
385 '"host-labels"')
386
showard35444862008-08-07 22:35:30 +0000387 return query_set
388
389
showard02813502008-08-20 20:52:56 +0000390 def query_test_ids(self, filter_data):
391 dicts = self.model.query_objects(filter_data).values('test_idx')
392 return [item['test_idx'] for item in dicts]
393
394
showard02813502008-08-20 20:52:56 +0000395 def query_test_label_ids(self, filter_data):
showardd50ffb42008-09-04 02:47:45 +0000396 query_set = self.model.query_objects(filter_data)
397 query_set = self._add_label_joins(query_set, suffix='_list')
398 rows = self._custom_select_query(query_set, ['test_labels_list.id'])
399 return [row[0] for row in rows if row[0] is not None]
showard02813502008-08-20 20:52:56 +0000400
401
showardeaccf8f2009-04-16 03:11:33 +0000402 def escape_user_sql(self, sql):
403 sql = super(TestViewManager, self).escape_user_sql(sql)
404 return sql.replace('test_idx', self.get_key_on_this_table('test_idx'))
405
406
showard35444862008-08-07 22:35:30 +0000407class TestView(dbmodels.Model, model_logic.ModelExtensions):
408 extra_fields = {
showardf4c702e2009-07-08 21:14:27 +0000409 'DATE(job_queued_time)': 'job queued day',
410 'DATE(test_finished_time)': 'test finished day',
showard35444862008-08-07 22:35:30 +0000411 }
412
413 group_fields = [
showardf4c702e2009-07-08 21:14:27 +0000414 'test_name',
415 'status',
416 'kernel',
417 'hostname',
418 'job_tag',
419 'job_name',
420 'platform',
421 'reason',
422 'job_owner',
423 'job_queued_time',
424 'DATE(job_queued_time)',
425 'test_started_time',
426 'test_finished_time',
427 'DATE(test_finished_time)',
showard35444862008-08-07 22:35:30 +0000428 ]
429
430 test_idx = dbmodels.IntegerField('test index', primary_key=True)
431 job_idx = dbmodels.IntegerField('job index', null=True, blank=True)
432 test_name = dbmodels.CharField(blank=True, maxlength=90)
433 subdir = dbmodels.CharField('subdirectory', blank=True, maxlength=180)
434 kernel_idx = dbmodels.IntegerField('kernel index')
435 status_idx = dbmodels.IntegerField('status index')
436 reason = dbmodels.CharField(blank=True, maxlength=3072)
437 machine_idx = dbmodels.IntegerField('host index')
438 test_started_time = dbmodels.DateTimeField(null=True, blank=True)
439 test_finished_time = dbmodels.DateTimeField(null=True, blank=True)
440 job_tag = dbmodels.CharField(blank=True, maxlength=300)
441 job_name = dbmodels.CharField(blank=True, maxlength=300)
442 job_owner = dbmodels.CharField('owner', blank=True, maxlength=240)
443 job_queued_time = dbmodels.DateTimeField(null=True, blank=True)
444 job_started_time = dbmodels.DateTimeField(null=True, blank=True)
445 job_finished_time = dbmodels.DateTimeField(null=True, blank=True)
446 hostname = dbmodels.CharField(blank=True, maxlength=300)
447 platform = dbmodels.CharField(blank=True, maxlength=240)
448 machine_owner = dbmodels.CharField(blank=True, maxlength=240)
449 kernel_hash = dbmodels.CharField(blank=True, maxlength=105)
450 kernel_base = dbmodels.CharField(blank=True, maxlength=90)
451 kernel = dbmodels.CharField(blank=True, maxlength=300)
452 status = dbmodels.CharField(blank=True, maxlength=30)
453
454 objects = TestViewManager()
455
456 def save(self):
457 raise NotImplementedError('TestView is read-only')
458
459
460 def delete(self):
461 raise NotImplementedError('TestView is read-only')
462
463
464 @classmethod
465 def query_objects(cls, filter_data, initial_query=None):
466 if initial_query is None:
showard64aeecd2008-09-19 21:32:58 +0000467 initial_query = cls.objects.get_query_set_with_joins(filter_data)
showard35444862008-08-07 22:35:30 +0000468 return super(TestView, cls).query_objects(filter_data,
469 initial_query=initial_query)
470
471
472 @classmethod
showarde732ee72008-09-23 19:15:43 +0000473 def list_objects(cls, filter_data, initial_query=None, fields=None):
474 # include extra fields
475 if fields is None:
476 fields = cls.get_field_dict().keys() + cls.extra_fields.keys()
477 return super(TestView, cls).list_objects(filter_data, initial_query,
478 fields)
showard35444862008-08-07 22:35:30 +0000479
480
481 class Meta:
482 db_table = 'test_view_2'