1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 __docformat__ = "epytext en"
132
133 """
134 This module defines one class, L{dbobject}, which is the base class
135 for all objects orm retrieves from or stores in the database.
136 """
137
138 from string import *
139 from types import *
140 import copy
141
142 from orm2 import sql, keys
143 from orm2.datasource import datasource_base
144 from orm2.util import stupid_dict, module_property
145 from orm2.exceptions import *
146 from orm2.datatypes import datatype
147 from orm2.ui import view, view_spec
148
150 """
151 This is the base class for all results. A result is a collection of one
152 kind of dbobjects that have been retrieved from the database. This class
153 will emulate a sequence but also has a next() method like a generator (so
154 I don't have to change all that code that assumes a result to be a
155 generator). The result class also has two methods to determine the
156 result's length by querying the database. If you need to traverse over a
157 result more than once, you must cast it into a list (and by that copying
158 all dbobjects to the client's memory).
159
160 The result class needs to deal with datasources that have an attribute
161 called no_fetchone set, that makes this class use the cursor.fetchall()
162 method (most notable for the gadfly adapter).
163 """
164
165 - def __init__(self, ds, dbclass, select):
166 """
167 @param ds: Datasource object
168 @param dbclass: dbclass object of whoes instances this result will be
169 @param select: orm2.sql.select instance of the query
170 """
171 self.ds = ds
172 self.dbclass = dbclass
173
174 if not isinstance(select, sql.select):
175 raise TypeError("result can only work on sql.select instances!")
176
177 self.select = select
178
179 self.columns = dbclass.__select_columns__()
180 self.cursor = ds.execute(select)
181
182 if getattr(self.ds, "no_fetchone", False):
183 self.rows = self.cursor.fetchall()
184 self.rows.reverse()
185
186 for name, prop in self.__class__.__dict__.items():
187
188
189
190 if ( type(prop) == ClassType and issubclass(prop, view) ):
191 setattr(self, name, prop(result=self))
192
193 if isinstance(prop, view_spec):
194 cls = prop.import_(name)
195 setattr(self, name, cls(result=self))
196
197
199 return self
200
202 if hasattr(self, "rows"):
203 if len(self.rows) == 0:
204 tpl = None
205 else:
206 tpl = self.rows.pop()
207 else:
208 tpl = self.cursor.fetchone()
209
210 if tpl is None:
211 raise StopIteration
212 else:
213 info = stupid_dict(zip(self.columns, tpl))
214 return self.dbclass.__from_result__(self.ds, info)
215
216 fetchone = next
217
219 """
220 This is a helper function that will perform a query as
221
222 SELECT COUNT(*) ...
223
224 appropriate to determine the number of rows in this result.
225 This will remove all clauses of the original select except the
226 WHERE clause.
227
228 This can't be called __len__(), because then it is used by
229 list() and yields a superflous SELECT query.
230 """
231 count_select = copy.deepcopy(self.select)
232 count_select.clauses = \
233 filter(lambda clause: isinstance(clause, sql.where),
234 count_select.clauses)
235 count_select.columns = sql.expression("COUNT(*)")
236 return int(self.ds.query_one(count_select))
237
238 count_all = count
239
240
241
243 """
244 Base class for all database aware classes.
245
246 It contains a number of helper methods which are called like this:
247 __help__(). You may safely add db-aware properties, regular properties
248 and methods.
249
250 @cvar __primary_key__: The primary key must be either
251 - a keys.primary_key instance
252 - a tuple of strings indicating attribute (not column!) names of this
253 class that form a multi column primary key
254 - a simple string indicating the attribute that manages the primary
255 key column of this dbclass
256 - None if the class does not have a primary key (which makes it
257 impossible to update rows by updating an instance's attributes
258 through orm)
259
260 @cvar __result__: This attribute must be a class which inherits
261 from result. It is used to represent results, sets of
262 this dbclass retrieved from the database. It will returned for all
263 calls to the datasource.run_select() method (which takes care of
264 all methods'select' in their names, except where explicitly noted.
265
266 @cvar __relation__: Name of the relation this dbclass' values are
267 stored in. Defaults to the class' name. May be set to a string or an
268 sql.relation instance.
269
270 @cvar __schema__: String containing the name of the schema this dbclass'
271 relatin resides in.
272 """
273
274 __primary_key__ = "id"
275 __result__ = result
276 __model__ = module_property()
277
319
320
322 """
323 Construct a dbobj from key word arguments. Example::
324
325 me = person(firstname='Diedrich', lastname='Vorberg')
326
327 firstname and lastname are dbproperties. The reserved parameter
328 __ds allows you to pass a datasource to objects that are not
329 inserted yet and might need a ds to construct views and the like.
330 """
331 if kw.has_key("__ds"):
332 __ds = kw["__ds"]
333 del kw["__ds"]
334
335 if not isinstance(__ds, datasource_base):
336 raise TypeError("__ds must be a subclass of "+\
337 "orm2.datasource.datasource_base")
338
339 else:
340 __ds = None
341
342 self._ds = __ds
343 self._is_stored = False
344
345 for name, prop in self.__class__.__dict__.iteritems():
346 if isinstance(prop, datatype) and not hasattr(prop, "dbclass"):
347 prop.__init_dbclass__(self.__class__, name)
348
349
350
351
352 if ( type(prop) == ClassType and issubclass(prop, view) ):
353 setattr(self, name, prop(self))
354
355 if isinstance(prop, view_spec):
356 cls = prop.import_(name)
357 setattr(self, name, cls(dbobj=self))
358
359 self.__update_from_dict__(kw)
360
361 if self.__primary_key__ is not None:
362 self.__primary_key__ = keys.primary_key(self)
363
364
366 """
367 This constructor is called by L{datasource.datasource_base}
368 when an object is created using a row retreived from the RDBMS.
369
370 @param ds: datasource we are created by (see select() method)
371 @param info: dictionary as { 'column_name': <data> }
372 """
373 self = cls(__ds=ds)
374 for property in cls.__dbproperties__():
375 if info.has_key(property.column):
376 property.__set_from_result__(ds, self, info[property.column])
377
378 self._ds = ds
379 self._is_stored = True
380
381 return self
382
383 __from_result__ = classmethod(__from_result__)
384
386 """
387 This method is called by datasource.insert() after the insert
388 query has been performed. It sets the dbobj's _ds attribute.
389
390 @param ds: datasource that just inserted us
391 """
392 self._ds = ds
393 self._is_stored = True
394
396 """
397 Return this dbobject's datasource (the one it is stored in).
398 """
399 if not hasattr(self, "_ds"):
400 raise ObjectMustBeInserted("...before you use __ds__()")
401
402 return self._ds
403
405 """
406 @returns: Wheather this dbobj has been stored in the database already
407 or retrieved from it
408 """
409 return self._is_stored
410
412 """
413 Return the datatype objects among this dbobjects attributes as a dict
414 like { name: property, ... }
415 """
416 for prop in cls.__dict__.values():
417 if isinstance(prop, datatype):
418 yield prop
419
420 __dbproperties__ = classmethod(__dbproperties__)
421
422
424 """
425 Return a dbproperty by its name. Raise exceptions if
426
427 - there is no property by that name
428 - it's not a dbproperty
429
430 name defaults to the dbclass' primary key.
431 """
432 if name is None:
433 if cls.__primary_key__ is None:
434 raise NoPrimaryKey()
435 else:
436 name = cls.__primary_key__
437
438 try:
439 property = cls.__dict__[name]
440 except KeyError:
441 tpl = ( repr(name), cls.__name__, )
442 raise AttributeError("No such attribute: %s (in class %s)" % tpl)
443
444 if not isinstance(property, datatype):
445 raise NoDbPropertyByThatName(name + " is not a orm2 datatype!")
446
447 return property
448
449 __dbproperty__ = classmethod(__dbproperty__)
450
452 """
453 Return whether this dbclass has a property named `name`.
454 """
455 try:
456 cls.__dbproperty__(name)
457 return True
458 except NoDbPropertyByThatName:
459 return False
460 except AttributeError:
461 return False
462
463
464 __has_dbproperty__ = classmethod(__has_dbproperty__)
465
466
482
483 __select_columns__ = classmethod(__select_columns__)
484
485
487 """
488 Return a human readable (more or less) representation of this
489 dbobject.
490 """
491 ret = []
492
493 ret.append("pyid=" + str(id(self)))
494
495
496
497
498
499
500 attribute_names = []
501 for name, value in self.__dict__.items():
502 if isinstance(value, datatype):
503 attribute_names.append(name)
504
505 for a in attribute_names:
506 b = a + "="
507
508 try:
509 val = getattr(self, a)
510
511
512
513
514 b += repr(val)
515 except AttributeError:
516 b += "<not set>"
517
518 ret.append(b)
519
520 return "<" + self.__class__.__name__ + " (" + \
521 join(ret, " ") + ")>"
522
523
525 """
526 Two dbobjects are considered equal, if they have the same dbclass
527 and the same primary key. B{This method does not check any
528 attributes!}
529 """
530 if self.__primary_key__ is None or other.__primary_key__ is None:
531 raise ValueError("Can't check equality on dbclasses that don't have a primary key")
532
533 if not self.__primary_key__.isset() or \
534 not other.__primary_key__.isset():
535 raise ValueError("Can't check equality on a dbobj whoes primary key is not yet set")
536
537 return self.__primary_key__.__eq__(other.__primary_key__)
538
540 """
541 Same as L{__eq__}, just the other way 'round ;-)
542 """
543 return (not self == other)
544
557
562
569
576