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 Defines a number of classes based on L{relationship} which model how
87 dbclasses interrelate.
88 """
89
90 import sys
91 from types import *
92
93 from orm2.datatypes import datatype
94 from orm2.exceptions import *
95 from orm2 import sql, keys
96 from orm2.ui import view
97
99 """
100 Base class for all relationships.
101
102 For documentation of the other parameters refer to the constructor
103 of L{datatype.datatype}.
104 """
105 - def __init__(self, child_class, child_key=None, foreign_key=None,
106 title=None, has_default=None):
107 """
108 @param child_class: The dbclass, this property's owner has a
109 relationship to
110 @param child_key: A string or a tuple of strings indicating those
111 properties of the child_class that are used in the foreign key. This
112 defaults to the child's primary key. The child_key must match the
113 foreign_key in the number of its members and the attribute's
114 datatypes.
115 @param foreign_key: The foreign key. Simmlar to a dbclass' primary key
116 this may be a simple string, indication the attribute that manages
117 the foreign key column or a tuple of strings indicating columns
118 that form the foreign key together. If the foreign key parameter is
119 None __init_dbclass__() will try to guess the foreign key name as
120 'child's table'_'primary key column'.
121
122 """
123 datatype.__init__(self, column=None, title=title, validators=(),
124 has_default=has_default)
125
126 self.child_class = child_class
127 self.child_key = child_key
128 self.foreign_key = foreign_key
129
130
131
133 raise NotImplementedError("Not implemented by this relationship.")
134
136 """
137 @returns: True. Most relationships are always set, even if they
138 return [] or the like.
139 """
140 return True
141
143 "Relationships do not need a convert method or can't use it anyway."
144 raise NotImplementedError(__doc__)
145
147 "This relationship cannot be represented as an SQL literal."
148 return None
149
151 """
152 @returns: False. Most relationships do not need to select anything.
153 """
154 return False
155
157 """
158 @returns: False. Most relationships to not need to select anything,
159 even after the insert.
160 """
161 return False
162
163
165
167 - def __init__(self, dbobj, relationship):
168 """
169 @param dbobj: The dbobj our parent is a property of.
170 @param relationship: The parent one2many relationship.
171 """
172 self.dbobj = dbobj
173 self.relationship = relationship
174
175
177 """
178 Return those child objects that are associated with the parent's
179 owner through their foreign key.
180 """
181 for dbobj in self.select():
182 yield dbobj
183
185 raise NotImplementedError()
186
187 - def len(self, *claises):
188 raise NotImplementedError()
189
190 - def append(self, *new_child_objects):
191 raise NotImplementedError()
192
194 """
195 Return the number of child dbobjects returned (='contained') in
196 this result. Note that a call to this function will yield a SQL
197 query seperate from the one used to retrieve the the actual
198 objects on every call.
199 """
200 return int(self.len())
201
203 return self.dbobj.__ds__()
204
207
210
212 """
213 Add our where clause to clauses. If there is a where clause
214 among the clauses already, it will be connected to our where
215 clause using AND.
216
217 @returns: A new set of clauses including the new where clause.
218 """
219 clauses = list(clauses)
220 where = False
221 for counter, clause in enumerate(clauses):
222 if isinstance(clause, sql.where):
223 where = True
224 clauses[counter] = self.where() + clause
225
226 if not where:
227 clauses.append(self.where())
228
229 return clauses
230
232 """
233 This function allows access to a dbclass' __result__'s views
234 through a relationship dbattribute.
235
236 >>> view = parent.relationship.view()
237
238 Will return an instance of child.__result__.view initialized
239 to the current result.
240 """
241 dbclass = self.relationship.child_class
242
243 if hasattr(dbclass.__result__, name):
244 view_cls = getattr(dbclass.__result__, name)
245 if type(view_cls) == ClassType and issubclass(view_cls, view):
246 return view_cls(result=self.select())
247
248 else:
249 raise AttributeError(name)
250
251
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269 pass
270
272 return self.result(dbobj, self)
273
275 raise NotImplementedError()
276
278 """
279 A one2many relationship is probably the most common relationship between
280 two tables in a RDBMS. For each row in table A table B contains zero or
281 more rows. This is implemented by defining a column in table B that
282 contains values that uniquly specify the row in table A they belong to.
283 This is called a 'foreign key', i.e. a key that belongs to a foreign
284 table.
285 """
286
288 - def __init__(self, dbobj, relationship):
322 """
323 This method allows you to add your own clauses to the SELECT
324 SQL statement that is used to retrieve the childobjects from the
325 database. This works just like the datasource's select() method,
326 with two exceptions:
327
328 - The dbclass is the relation's childclass.
329 - If you supply a WHERE clause it will be added to the WHERE
330 clause generated by the one2many relationship using the AND
331 conjunction. This WHERE clause is available through this
332 class' where() method.
333
334 Example::
335
336 country.cities.select(order_by='name')
337
338 """
339 clauses = self.add_where(clauses)
340 return self.ds().select(self.child_class(), *clauses)
341
342 - def len(self, *clauses):
343 """
344 Return the number of child objects that would be returned by
345 the select() method using clauses. Note that a call to this
346 function will yield a SQL query seperate from the one used to
347 actually retrieve the dbobjects. (See datasource_base.count() for
348 details)
349 """
350 clauses = self.add_where(clauses)
351 return self.ds().count(self.child_class(), *clauses)
352
353 - def append(self, *new_child_objects):
354 """
355 Append new child objects to a one2many relationship. Those
356 objects must not have been inserted into the database, yet,
357 because otherwise they would just be moved from one parent object
358 to the other...
359 """
360 for a in new_child_objects:
361 if a.__class__ != self.child_class():
362 raise TypeError("You can only add %s instances to this " +\
363 "relationship" % repr(self.child_class()))
364
365 if a.__is_stored__():
366 raise ValueError("You can't append objects to a one2many"+\
367 " relationship that are already stored"+\
368 " in the database.")
369
370 for a in new_child_objects:
371 items = zip(self.foreign_key.other_attribute_names(),
372 self.foreign_key.values())
373 for (attribute_name, value) in items:
374 setattr(a, attribute_name, value)
375
376 self.ds().insert(a)
377
379 pass
380
382 """
383 Setting a one2many relationship needs three steps:
384
385 1. value must be a list of new (not-yet inserted) child objects
386 2. delete all child objects from the db
387 3. insert all child objects on the list with the foreign key
388 pointing to dbobj
389 """
390 raise NotImplementedError("Not implemented, yet")
391
392
393
395 - def __init__(self, child_class, child_key=None, foreign_key=None,
396 title=None, has_default=None):
409
410
412 foreign_key = keys.foreign_key(dbobj,
413 self.child_class,
414 self.foreign_key,
415 self.child_key)
416
417 if isinstance(value, self.child_class):
418 if not value.__is_stored__():
419 raise ObjectMustBeInserted("To set a many2one/one2one value "
420 "the child object must have been "
421 "inserted into the database.")
422 setattr(dbobj, self.data_attribute_name(), value)
423
424 for my_attr, child_attr in zip(foreign_key.my_attribute_names(),
425 foreign_key.other_attribute_names()):
426 setattr(dbobj, my_attr, getattr(value, child_attr))
427
428 elif value is None:
429 for my_attr in foreign_key.my_attribute_names():
430 setattr(dbobj, my_attr, None)
431
432 dbobj.__ds__().flush_updates()
433
434 else:
435 msg = "The %s attribute can only be set to objects of class %s"
436 msg = msg % ( self.attribute_name, self.child_class.__name__, )
437 raise ValueError(msg)
438
440 if self.isset(dbobj):
441
442 if hasattr(dbobj, self.data_attribute_name()):
443 return getattr(dbobj, self.data_attribute_name())
444 else:
445 foreign_key = keys.foreign_key(dbobj,
446 self.child_class,
447 self.foreign_key,
448 self.child_key)
449
450 child = dbobj.__ds__().select_one(self.child_class,
451 foreign_key.other_where())
452
453 setattr(dbobj, self.data_attribute_name(), child)
454
455 return child
456
457 else:
458
459 relationship.__get__(self, dbobj, owner)
460
461 one2one = many2one
462
464 """
465 The many to many relationship manages rows from two tables which
466 are linked by means of a third table, the link_relation. This tables
467 stores keys from each of the tables that are to be linked. Note that
468 these keys must be single column keys.
469
470 If you set a many2many property to a list of child objects or append one
471 it will be inserted into the databse if need be.
472
473 The result class implements a subset of the list interface, but the
474 lists elements are considered to have no guaranteed order (as the values
475 in a dict have no order).
476 """
477
479 """
480 Instances of this class are returned if you __get__ a many2many
481 dbproperty.
482 """
484 """
485 Use like this:
486
487 >>> result = dbobj.children.select(sql.where(...))
488
489 This will yield those child objects that fit the condition
490 in the where statements. Note that your WHERE will be
491 integrated into a more complex WHERE clause. The many2many
492 relationship uses a LEFT JOIN to connect the link_relation
493 and the child relation. You must do that by hand. Also,
494 doing so might mess up your db, so you might want to use
495 FOREIGN KEY constraints on the link relation.
496 """
497 relations = ( self.relationship.link_relation,
498 self.child_class().__relation__, )
499 clauses = self.add_where(clauses)
500
501 query = sql.select(
502 self.child_class().__select_columns__(),
503 relations, *clauses)
504
505 return self.ds().run_select(
506 self.child_class(), query)
507
508
509 - def len(self, *clauses):
510 """
511 Return the number of child objects associated with a parent.
512 You may supply a where clause. The same things apply as for the
513 where clause for select(), see above.
514 """
515 clauses = self.add_where(clauses)
516
517 query = sql.select("COUNT(*)",
518 ( self.relationship.link_relation,
519 self.child_class().__relation__, ),
520 *clauses)
521
522 return self.ds().query_one(query)
523
544
545
546 - def all(self, *clauses):
547 """
548 This method will return all entries in the child relation (or a
549 subset specified by clauses) and a list of those primary keys
550 which are present in the link table. You can check if a dbobj is
551 linked by doing:
552
553 >>> result, active_keys = dbobj.relation.all()
554 >>> for a in result:
555 ... if a.__primary_key__.values() in active_keys:
556 ... do_something(a)
557 ... else:
558 ... do_somethin_else(a)
559
560
561 """
562 if self.dbobj.__is_stored__():
563 relations = ( self.relationship.link_relation,
564 self.child_class().__relation__, )
565 join_clauses = self.add_where(clauses)
566
567 child_pkey = keys.primary_key(self.child_class())
568 query = sql.select( tuple(child_pkey.columns()),
569 relations, *join_clauses)
570
571 cursor = self.ds().execute(query)
572 active_keys = list(cursor.fetchall())
573 else:
574 active_keys = []
575
576 result = self.ds().select(self.child_class(), *clauses)
577
578 return ( result, active_keys, )
579
580 - def append(self, *new_child_objects):
604
605 - def unlink(self, child_object):
612
633
634
635
636 - def __init__(self, child_class, link_relation,
637 parent_own_key=None, parent_link_column=None,
638 child_own_key=None, child_link_column=None,
639 title=None):
640 """
641 @param parent_own_key: The attribute in the parent dbclass that
642 is referred to by the link table. Defaults to the primary key.
643 @param parent_link_column: The column name(!) in the link table
644 referring to parent_own_key. Defaults to
645 <parent class name>_<primary key column>.
646 @param child_own_key: The attribute in the child dbclass that
647 is referred to by the link table. Defaults to the primary key.
648 @param child_link_column: The column name(!) in the link table
649 referring to child_own_key. Defaults to
650 <child class name>_<child key column>.
651 """
652 relationship.__init__(self, child_class, None, None,
653 title, False)
654
655 if isinstance(link_relation, sql.relation):
656 self.link_relation = link_relation
657 else:
658 self.link_relation = sql.relation(link_relation)
659
660 self._parent_own_key = parent_own_key
661
662 if isinstance(parent_link_column, sql.column):
663 self._parent_link_column = parent_link_column.column
664 else:
665 self._parent_link_column = parent_link_column
666
667 self._child_own_key = child_own_key
668
669 if isinstance(child_link_column, sql.column):
670 self._child_link_column = child_link_column.column
671 else:
672 self._child_link_column = child_link_column
673
674
693
694 - def reverse(cls, original_dbclass, attribute_name,
695 title=None):
696 """
697 Constructor.
698
699 A little helper function: If you've defined one many2many relation,
700 this constructor will take it as an argument and return the
701 complimentary one to be an attribute in the child class.
702
703 @param original_dbclass: dbclass you've already defined a many2many
704 relationship for.
705 @param attribute_name: Attribute name of the many2many relationship
706 in the original dbclass.
707 """
708
709 original = original_dbclass.__dbproperty__(attribute_name)
710
711 return many2many(original_dbclass,
712 original.link_relation,
713 original._child_own_key,
714 original._child_link_column,
715 original._parent_own_key,
716 original._parent_link_column,
717 title)
718
719 reverse = classmethod(reverse)
720
726
728 if self._parent_link_column is None:
729 return "%s_%s" % ( dbobj.__class__.__name__,
730 dbobj.__primary_key__.column().name, )
731 else:
732 return self._parent_link_column
733
739
749
750
751
752
754 - def __init__(self, child_class,
755 child_key=None, foreign_key=None, column=None,
756 title=None, has_default=None,
757 cache=True):
784
787
800
801
802
804 if self.cache and \
805 hasattr(dbobj, self.data_attribute_name() + "_cache"):
806 return getattr(dbobj, self.data_attribute_name() + "_cache")
807
808 ds = dbobj.__ds__()
809
810 if self.column is not None:
811 value = datatype.__get__(self, dbobj)
812 ret = ds.select_by_primary_key(self.child_class, value)
813 else:
814 foreign_key = keys.foreign_key(dbobj, self.child_class,
815 self.foreign_key, self.child_key)
816
817
818 set_count = 0
819 none_count = 0
820 for attr in foreign_key.my_attributes():
821 if attr.__get__(dbobj) is None:
822 none_count += 1
823 else:
824 set_count += 1
825
826 if set_count == 0:
827 return None
828
829
830
831
832 if none_count != 0:
833
834
835 raise IllegalForeignKey("For a many2one relationship with a "+\
836 "multi column key, either all attrs "+\
837 "must be set or all must be None.")
838
839 result = ds.select(self.child_class,
840 foreign_key.other_where())
841
842 try:
843 ret = result.next()
844 except StopIteration:
845 raise IllegalForeignKey("The foreign key you set for " + \
846 repr(dbobj) + " does not refer to " + \
847 "exaxtly one child object")
848
849 if self.cache:
850 setattr(dbobj, self.data_attribute_name() + "_cache", ret)
851
852 return ret
853
855 """
856 Set the child object to `value'. If the child object has not been
857 inserted, yet it will be by this function.
858 """
859 if value is None:
860 if self.column is not None:
861 datatype.__set__(self, dbobj, None)
862 else:
863 foreign_key = keys.foreign_key(dbobj, self.child_class,
864 self.foreign_key,
865 self.child_key)
866
867 for prop in foreign_key.my_attributes():
868 prop.__set__(dbobj, None)
869
870 if hasattr(dbobj, self.data_attribute_name() + "_cache"):
871 delattr(dbobj, self.data_attribute_name() + "_cache")
872 else:
873 if not isinstance(value, self.child_class):
874 raise TypeError("A many2one attribute can only be set to " + \
875 self.child_class.__name__, " + instances")
876
877 if not value.__is_stored__():
878 self.__ds__().insert(value)
879
880 if self.column is not None:
881 datatype.__set__(self, dbobj, value.__primary_key__.
882 attribute().__get__(value))
883 else:
884 foreign_key = keys.foreign_key(dbobj, self.child_class,
885 self.foreign_key,
886 self.child_key)
887
888 for my, other in zip(foreign_key.my_attributes(),
889 foreign_key.other_attributes()):
890 my.__set__(dbobj, other.__get__(value))
891
892 if self.cache:
893 setattr(dbobj, self.data_attribute_name() + "_cache", value)
894
895
904
911
917
919 if self.column is not None:
920 return True
921 else:
922 return False
923
924
925
926
927
928
929
930
931