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 These classes are very simmilar to primitives in that they manage two
41 relations. The child relation however is not represented by a dbclass,
42 but only by a primitive type.
43 """
44
45 import sys
46 from types import *
47
48 from orm2.dbobject import dbobject
49 from orm2.datatypes import datatype
50 from orm2.exceptions import *
51 from orm2 import sql, keys
52
54 - def __init__(self, child_relation, child_column,
55 child_key=None, title=None):
56 """
57 @param child_relation: sql.relation object or string, indicating
58 the name of the dependent relation.
59 @param child_column: It's datatype (datatype object). The datatype's
60 column parameter may be used as well as teh validators. Title and
61 has_default will be ignored. The column name defaults to the
62 container's attribute_name in the parent dbclass.
63 @param child_key: A string indicating the column in the child
64 relation that is used in the foreign key to point to the parent.
65
66 The the docstrings of the actual implementations for examples,
67 that's going to make it clearer.
68
69 As a sidenote: This only works for a single-column reference
70 key from the parent class to the child class. It would be
71 possible implementing this for multiple column keys using an
72 anonymous dbclass, but it's just soooo darn complicated! So I
73 thought to myself that orm2 is complecated enough...
74 """
75 datatype.__init__(self, None, title)
76 self.child_relation = child_relation
77 self.child_column = child_column
78 self.child_key = child_key
79
80
90
92 """
93 A container is not selected from a result.
94 """
95 raise NotImplementedError("Not implemented by this container datatype.")
96
98 "Containers do not need a convert method or can't use it anyway."
99 raise NotImplementedError(__doc__)
100
102 "This container cannot be represented as an SQL literal."
103 return None
104
106 """
107 @returns: False. Containers do not need to select anything.
108 """
109 return False
110
112 """
113 @returns: False. Containers to not need to select anything,
114 even after the insert.
115 """
116 return False
117
119 """
120 A where clause that leads to all the rows in the child table
121 associated with this parent table.
122 """
123 return sql.where( self.child_key, " = ",
124 dbobj.__primary_key__.sql_literal() )
125
126
128 """
129 This datatype represents a relationship between relations in which
130 the child relation provides a number of values for a parent
131 relation indexed by the parent's primary key. In the parent dbobject
132 these values appear as a tuple.
133
134 Like this::
135
136 CREATE parent (
137 id INTEGER,
138 ... some more fields ...
139 );
140
141 CREATE child (
142 parent_id INTEGER NOT NULL REFERENCES parent(id),
143 info TEXT
144 );
145
146 An appropriate dbclass would look like this::
147
148 class parent(dbobject):
149 id = integer()
150 info = sqltuple('child', text(), 'id', 'parent_id')
151
152 result = ds.select(parent)
153 p = result.next()
154 p.info = ( "One", "Two", "Three", )
155
156 An sqltuple is not mutable (i.e. a tuple and not a list), so you
157 can't set any member of the tupe as in t[3] = 'Hallo'. To append a
158 value you must say dbobj.tpl += ( "Hallo", ). The program will do
159 the right thing and only create as many INSERT statements as
160 strings added. For all other operations all rows referenced by the
161 parent's key will be DELETEd and INSERTed again.
162
163 The sqltuple dbattribute may be set to any iterable that yields
164 values of the appropriate type. It will always return a Python
165 tuple.
166
167 The orderby parameter allows you to specify an SQL clause that
168 will determine the order of the child table's rows that are
169 returned. It may be None. Orderby must be an instance of
170 sql.orderby.
171
172 An sqltuple cannot be None, just an empty tuple.
173 """
174 - def __init__(self, child_relation, child_column, orderby=None,
175 child_key=None, title=None):
176 _container.__init__(self, child_relation, child_column,
177 child_key, title)
178 assert orderby is None or isinstance(orderby, sql.orderby), \
179 "Orderby must be an instance of sql.orderby"
180 self.orderby = orderby
181
182
183 - def __get__(self, dbobj, owner="What??"):
201
202 - def __set__(self, dbobj, new_values):
203 self.check_dbobj(dbobj)
204
205 if new_values is None:
206 raise ValueError("You cannot set a sqltuple to None, sorry.")
207
208
209
210
211 new = []
212 for value in new_values:
213 value = self.child_column.__convert__(value)
214 for validator in self.child_column.validators:
215 validator.check(dbobj, self, value)
216
217 new.append(value)
218
219
220
221
222
223 if self.isset(dbobj):
224 dont_delete = True
225 old = getattr(dbobj, self.data_attribute_name())
226
227 if len(old) <= len(new):
228 for o, n in zip(old, new):
229 if o != n:
230 dont_delete = False
231 break
232 else:
233 old = ()
234 dont_delete = False
235
236 if dont_delete:
237 to_insert = new[len(old):]
238 else:
239 to_insert = new
240
241 dbobj.__ds__().execute(sql.delete(self.child_relation,
242 self.child_where(dbobj)))
243
244 for value in to_insert:
245 if value is None:
246 literal = sql.NULL
247 else:
248 literal = self.child_column.sql_literal_class(value)
249
250 dbobj.__ds__().execute(
251 sql.insert(self.child_relation,
252 ( self.child_key,
253 self.child_column.column, ),
254 ( dbobj.__primary_key__.sql_literal(),
255 literal, ) ))
256
257 setattr(dbobj, self.data_attribute_name(), tuple(new))
258
259
261 """
262 This datatype represents a relationship between relations in which
263 the child relation proveies a number of values for a parent
264 relation indexed by the parent's primary key and a unique key for
265 each of the entries. In the parent dbobject these key/value pairs
266 appear as a dict.
267
268 Like this:
269
270 CREATE user (
271 id INTEGER,
272 ... some more fields ...
273 );
274
275 CREATE user_info (
276 user_id INTEGER REFERENCES user(id),
277 key VARCHAR(100),
278 value TEXT,
279
280 PRIMARY KEY(user_id, key)
281 );
282
283 The PRIMARY KEY clause is not strictly necessary, but it will make
284 sure every user_id/key pair only appears once. You may also want to
285 create an index over the user_id column, because queries will
286 usually call for all the child rows associated with the parent.
287
288 An appropriate dbclass would look like this::
289
290 class user(dbobject):
291 id = integer()
292 info = sqldict('user_info', varchar(column=key),
293 Unicode(column=value))
294
295 result = ds.select(user)
296 me = result.next()
297 me.info['firstname'] = 'Diedrich'
298 me.info['lastname'] = 'Vorberg'
299
300 if me.info['age'] > 30:
301 print 'Consider suicide!'
302
303 As for the sqltuple the whole thing only works on single-column
304 primary keys.
305 """
306 - def __init__(self, child_relation, child_key_column, child_value_column,
307 child_key=None, title=None):
308 _container.__init__(self, child_relation, child_column=None,
309 child_key=child_key, title=title)
310 self.child_key_column = child_key_column
311 self.child_value_column = child_value_column
312
318
319 - def __get__(self, dbobj, owner="Who??"):
320 if dbobj is None: return self
321 self.check_dbobj(dbobj)
322
323 if self.isset(dbobj):
324 return getattr(dbobj, self.data_attribute_name())
325 else:
326
327 query = sql.select( ( self.child_key_column.column,
328 self.child_value_column.column, ),
329 self.child_relation,
330 self.child_where(dbobj) )
331 cursor = dbobj.__ds__().execute(query)
332 ret = map(lambda tpl:
333 ( self.child_key_column.__convert__(tpl[0]),
334 self.child_value_column.__convert__(tpl[1]), ),
335 cursor.fetchall())
336 ret = self.sqldict_dict(self, dbobj, dict(ret))
337 setattr(dbobj, self.data_attribute_name(), ret)
338 return ret
339
340
341 - def __set__(self, dbobj, new_dict):
342 self.check_dbobj(dbobj)
343
344 if new_values is None:
345 raise ValueError("You cannot set a sqldict to None, sorry.")
346
347
348
349
350 new = {}
351 for key, value in new_dict.items():
352 key = self.child_key_column.__convert__(key)
353 value = self.child_value_column.__convert__(value)
354
355 for validator in self.child_key_column.validators:
356 validator.check(dbobj, self, key)
357
358 for validator in self.child_value_column.validators:
359 validator.check(dbobj, self, value)
360
361 new[key] = value
362
363
364 dbobj.__ds__().execute(sql.delete(self.child_relation,
365 self.child_where(dbobj)))
366
367
368 for key, value in new.items():
369 key_literal = self.child_key_column.sql_literal_class(key)
370
371 if value is None:
372 value_literal = sql.NULL
373 else:
374 value_literal = self.child_value_column.sql_literal_class(value)
375
376 query = sql.insert( self.child_relation,
377 ( self.child_key,
378 self.child_key_columnd.column,
379 self.child_value_column.column, ),
380 ( dbobj.__primary_key__.sql_literal(),
381 key_literal, value_literal, ) )
382 dbobj.__ds__().execute(query)
383
384 setattr(dbobj, self.data_attribute_name(),
385 self.sqldict_dict(self, dbobj, new))
386
387
389 - def __init__(self, sqldict, dbobj, data={}):
390 self.update(data)
391 self._sqldict = sqldict
392 self._dbobj = dbobj
393
395 key_column = self._sqldict.child_key_column.column
396 key_literal = self._sqldict.child_key_column.sql_literal_class(key)
397
398 value_column = self._sqldict.child_value_column.column
399 value_literal = self._sqldict.child_value_column.sql_literal_class(
400 value)
401
402 if self.has_key(key):
403 where = self._sqldict.child_where(self._dbobj) + sql.where(
404 key_column, " = ", key_literal)
405
406 command = sql.update(self._sqldict.child_relation, where,
407 { str(value_column): value_literal })
408 else:
409 command = sql.insert( self._sqldict.child_relation,
410 ( self._sqldict.child_key,
411 key_column,
412 value_column, ),
413 ( self._dbobj.__primary_key__.sql_literal(),
414 key_literal, value_literal, ) )
415
416 self._dbobj.__ds__().execute(command)
417
418 dict.__setitem__(self, key, value)
419
430