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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157 __docformat__ = "epytext en"
158
159 """
160 Datatype classes for the default SQL datatypes.
161 ===============================================
162
163 Each of the classes in this module models an SQL datatype. Their
164 instances will be responsible for managing an attribute in a
165 dbclass. The classes accept a number of arguments for you to
166 influence what exactly they do. Refer to L{datatype.__init__} for these.
167
168 """
169
170 import sys, copy
171 from types import *
172 from string import *
173 from datetime import date as py_date
174 from datetime import datetime as py_datetime
175 from datetime import timedelta as py_timedelta
176
177 try:
178 from mx.DateTime import DateTimeType
179 from mx.DateTime import DateTimeDeltaType
180 except ImportError:
181 DateTimeType = None
182 DateTimeDeltaType = None
183
184 from orm2 import sql
185 from orm2.validators import validator, length_validator
186 from orm2.exceptions import *
187 from orm2.ui import widget
188
190 """
191 This class encapsulates a dbclass' property (=attribute). It takes
192 care of the SQL column name and the information
193 actually stored in the database/the dbobject.
194 """
195 python_class = None
196 sql_literal_class = None
197
198 - def __init__(self, column=None, title=None,
199 validators=(), has_default=False):
200 """
201 @param column: A orm2.sql column instance or a string containing a SQL
202 column name pointing to the column this property is
203 responsible for. Defaults to the column with the same name
204 as the attribute.
205 @param title: The title of this column used in dialogs, tables and
206 validator error messages (among other things). This must be a
207 unicode object or None.
208 @param validators: A sequence of objects of validators.validator
209 children. (A single validator is ok, too)
210 @param has_default: Boolean property that determines whether this
211 dbproperty is retrieved from the database after the dbobject has
212 been INSERTed. (So has_default referrs to the SQL column really).
213 """
214 if type(column) == StringType:
215 self.column = sql.column(column)
216 elif isinstance(column, sql.column):
217 self.column = column
218 elif column is None:
219 self.column = None
220 else:
221 raise TypeError("Column must either be a string or an sql.column"+\
222 " instance, not %s (%s)" % ( repr(type(column)),
223 repr(column),) )
224 self.title = title
225
226 if isinstance(validators, validator):
227 self.validators = ( validators, )
228 else:
229 self.validators = tuple(validators)
230
231 self.has_default = has_default
232
251
252
253
259
260 - def __get__(self, dbobj, owner="owner? Like owner of what??"):
261 """
262 See the Python Language Reference, chapter 3.3.2.2 for details on
263 how this works. Be sure to be in a relaxed, ready-for-hard-figuring
264 mood.
265 """
266
267
268
269
270
271 if dbobj is None: return self
272 self.check_dbobj(dbobj)
273
274 if self.isset(dbobj):
275 return getattr(dbobj, self.data_attribute_name())
276 else:
277 primary_key_property = repr(tuple(dbobj.__primary_key__.\
278 attribute_names()))
279 if not dbobj.__primary_key__.isset():
280 pk_literal = "<unset>"
281 else:
282 pk_literal = repr(tuple(dbobj.__primary_key__.values()))
283
284 tpl = ( self.attribute_name,
285 dbobj.__class__.__name__,
286 primary_key_property,
287 pk_literal, )
288
289 msg = "Attribute '%s' of '%s' [ %s=%s ] has not yet been set" % tpl
290
291 raise AttributeError( msg )
292
313
314
317
319 if self.attribute_name is not None and \
320 not self in dbobj.__dbproperties__():
321 msg = "dbclass '%s' does not have attribute '%s' (wrong " + \
322 "dbclass for this dbproperty!)"
323 msg = msg % ( dbobj.__class__.__name__, self.attribute_name, )
324 raise AttributeError(msg)
325
327 """
328 @returns: True, if this property is set, otherwise... well.. False.
329 """
330 return hasattr(dbobj, self.data_attribute_name())
331
333 """
334 Return value converted as a Python object of the class assigned to
335 this datatype.
336 """
337 if not isinstance(value, self.python_class):
338 return self.python_class(value)
339 else:
340 return value
341
343 """
344 Return an SQL literal representing the data managed by this property
345 in dbobj.
346
347 @returns: SQL literal as a string.
348 """
349
350 if not self.isset(dbobj):
351 msg = "This attribute has not been retrieved from the database."
352 raise AttributeError(msg)
353 else:
354 value = getattr(dbobj, self.data_attribute_name())
355
356 if value is None:
357 return sql.NULL
358 else:
359 return self.sql_literal_class(value)
360
362 """
363 Indicate whether this column shall be included in SELECT statements.
364 True by default, it will return False for most relationships.
365 """
366 return True
367
369 """
370 Indicate whether this column needs to be SELECTed after the dbobj has
371 been inserted to pick up information supplied by backend as by SQL
372 default values and auto increment columns.
373 """
374 return self.has_default and not self.isset(dbobj)
375
377 raise NotImplementedError(
378 "Can't delete a database property from a dbobj.")
379
382
387
392
399
400 -class Long(datatype):
406
413
414 real = Float
415
422
423 text = string
424
426 """
427 dbclass property for string values with a fixed (maximum-)length.
428 This is the string class above with a length_validator added.
429 """
430 - def __init__(self, max_length, column=None, title=None,
431 validators=(), has_default=False):
436
437 char = varchar
438
440 """
441 dbclass property for TEXT, VARCHAR, CHAR() etc. SQL columns that
442 are to be converted from SQL literals (i.e. encoded strings) to
443 Python Unicode objectes and vice versa.
444
445 When setting a Unicode property of a dbobj, you might want to convert
446 the value to Unicode yourself. This class uses Python's default encoding
447 (see documentation for sys.getdefaultencoding()) to convert things *to*
448 Unicode, which may or may not be what you want.
449 """
450 python_class = unicode
451 sql_literal_class = sql.unicode_literal
452
458
460 if type(value) != UnicodeType:
461 try:
462 return unicode(value)
463 except UnicodeDecodeError:
464 raise ValueError("This string can't be converted "+\
465 "to unicode using sys.defaultencoding. You must use the "+\
466 "unicode() function with a specific encoding to convert it"+\
467 " or set the default encoding. (%s)" % repr(value))
468 else:
469 return value
470
471
473 """
474 This is the baseclass for datetime, date, and time. Writing a
475 datetime datatype is more intricate matter than one would expect
476 on first sight. The challenges that come with it are, among other
477 things:
478
479 - that there are different calendars, Gregorian, Julian, Arabic and
480 others, which were/are used historically and locally
481 - that time is pretty much linear, but not entirely: There have been
482 corrections in history, large and small, as well as leap years and
483 seconds
484 - There are numerous different conventions on how to write date and
485 time, which are even ambiguous.
486
487 SQL backends approach these problems in diverse ways, which must
488 be handled by specific classes in the backend's datatype
489 module. Fortunately they all share a common denominator which I'm
490 trying to work with here. There are three classes: date, time and
491 datetime. On the Python side they use the datetime module (the
492 timedelta, date and datetime classes for the time, data and
493 datetime datatype respectively). Values provided as mx.DateTime
494 instances by the database will be converted. Towards the database
495 it will convert these into ISO compliant date representations,
496 quoted like SQL string literals. This is going to just work in
497 most situations.
498
499 If anyone feels like writing custom datetime datatypes for
500 specific backends, mapping Python's new datatypes to PostgreSQL's
501 timezone or DateTimeDelta capabilities or MySQL's ability to store
502 illegal dates (with 0s in it), they are very welcome!
503 """
515
519
521 raise NotImplementedError()
522
524 raise NotImplementedError()
525
526
528 """
529 Date and time without a timezone.
530
531 See L{datetime_base} for details.
532 """
535
537 if isinstance(value, py_datetime):
538 return value
539 elif hasattr(value, "year") and hasattr(value, "month") \
540 and hasattr(value, "day") and hasattr(value, "hour") \
541 and hasattr(value, "minute") and hasattr(value, "second"):
542
543 if type(value.year) == MethodType:
544 return py_datetime(value.year(),
545 value.month(),
546 value.day(),
547 value.hour(),
548 value.minute(),
549 value.second())
550 else:
551 second = int(value.second)
552 fraction = value.second - float(second)
553 milisecond = int(fraction * 1000)
554
555 return py_datetime(value.year,
556 value.month,
557 value.day,
558 value.hour,
559 value.minute,
560 second, milisecond)
561
562 else:
563 raise ValueError("This dbattribute may only be set to "+\
564 "datetime.datetime instances, not %s!" % \
565 repr(type(value)))
566
567
568 -class date(datetime):
569 """
570 date (resolution of one day)
571
572 See L{datetime_base} for details.
573 """
575 return value.strftime("%Y-%m-%d")
576
578 if isinstance(value, py_date):
579 return value
580 elif isinstance(value, py_datetime):
581 return py_date(value.year, value.month, value.day)
582 elif hasattr(value, "year") and hasattr(value, "month") \
583 and hasattr(value, "day"):
584 if type(value.year) == MethodType:
585 return py_date(value.year(),
586 value.month(),
587 value.day())
588 else:
589 return py_date(value.year,
590 value.month,
591 value.day)
592 else:
593 raise ValueError("This dbattribute may only be set to "+\
594 "datetime.date instances, not %s!" % \
595 repr(value))
596
597 -class time(datetime_base):
598 """
599 Time of day, 00:00:00 to 23:59:59
600 The resolution of the time class is limited to one second.
601
602 See L{datetime_base} for details.
603 """
606
608 if isinstance(value, py_timedelta):
609 return value
610 elif DateTimeDeltaType is not None and \
611 type(value) == DateTimeDeltaType:
612 ret = py_timedelta(0,
613 hours=value.hour,
614 minutes=value.minute,
615 seconds=value.second)
616 return ret
617 else:
618 raise ValueError("This dbattribute may only be set to "+\
619 "datetime.datetime instances, not %s!" % \
620 repr(type(value)))
621
625
626 Boolean = boolean
627
629 """
630 The common_serial datatype is an primary key integer column whoes
631 value is supplied by the backend using its default mechanism. The
632 default mechanism for each backend is defined by the adapter's
633 datatype module (see there). The name of the common_serial column is
634 alway 'id'.
635
636 This class used by some of the test cases to define data models that
637 work on every backend.
638
639 - For gadfly this works on a regular INTEGER column and is not suitable
640 for multi user operations (see L{orm2.adapters.gadfly.datasource}
641 for details)
642 - For postgresql this works on a SERIAL column named id.
643 - For mysql this works on an INTEGER column with AUTO_INCREMENT set.
644 - For firebird this works with an INTEGER column which is
645 combined with a sequence as described U{here
646 <http://firebird.sourceforge.net/index.php?op=faq#q0011.dat>}. The
647 sequence must be named GEN_PK_<tablename>. This is basically
648 the same what L{orm2.adapter.firebird.datatypes.serial} does,
649 except for the naming scheme for the generator.
650 """
651
655
660
662 raise NotImplementedError("common_serial values are always " + \
663 "retrieved from the database backend")
664
667
668
671 self.instance = instance
672 self.method = method
673
675 return self.method(self.instance, *args, **kw)
676
678 """
679 This is the base class for those datatype that are 'wrappers' for regular
680 datatypes. This class will forward all attribute access to the inner
681 datatype, including method calls. Except for those methods and attributes
682 it contains itself.
683
684 All classes derived from wrapper must overload the __copy__ method for
685 dbclass inheritance to work properly.
686 """
687
689 self.inside_datatype = inside_datatype
690
692 my_dict = object.__getattribute__(self, "__dict__")
693 my_cls = object.__getattribute__(self, "__class__")
694 my_cls_dict = my_cls.__dict__
695 inside_datatype = object.__getattribute__(self, "inside_datatype")
696
697 if my_dict.has_key(name):
698 return my_dict[name]
699
700 elif my_cls_dict.has_key(name):
701 ret = my_cls_dict[name]
702 if type(ret) == FunctionType:
703 return _inside_method(self, ret)
704 else:
705 return ret
706
707 elif wrapper.__dict__.has_key(name):
708 return _inside_method(self, wrapper.__dict__[name])
709
710 elif hasattr(inside_datatype, name):
711 return getattr(self.inside_datatype, name)
712
713 else:
714 raise AttributeError(name)
715
717 """
718 This will let the in in L{datatype.check_dbobj} yield True.
719 """
720 return other == self.inside_datatype
721
723 raise NotImplementedError( "All classes derived from wrapper must "
724 "overload the __copy__ method for "
725 "dbclass inheritance to work properly." )
726
732
733
735 """
736 This is a pseudy-datatype that takes an actual datatype as argument.
737 Values from that datatypes's column will not be SELECTed from the database
738 regularly, but only on attribute access. This way you can treat a dbclass
739 that contains large amount of data just like all the others and only
740 load the data at the point in time when it's needed.
741 """
742 - def __init__(self, inside_datatype, cache=False):
743 """
744 @param inside_datatype: The datatype <b>instance</b> this wrapper is
745 responsible for
746 @param cache: Parameter that determines whether the data is kept in
747 memory once it is loaded from the database
748 """
749 wrapper.__init__(self, inside_datatype)
750 self.cache = cache
751
752 - def __get__(self, dbobj, owner="I don't know what this is for"):
788
790 return False
791
793 return False
794
796 return delayed(copy(self.inside_datatype), self.cache)
797
799 """
800 Insert an arbitrary SQL expression into the SQL query.
801
802 The expression string may contain placeholders $relation, $table
803 (which are the same thing) and $attribute_name which will be replaced
804 prior to execution by appropriate strings. This is especially usefull
805 when using inheritance.
806
807 The has_default and default parameters may be used to supply a
808 default value which the expression dbproperty will contain it the
809 dbobject has not been SELECTed from the database. Has_default must
810 be set to True to use this feature. Default defaults to None ;-)
811 """
812 - def __init__(self, inside_datatype, expression,
813 has_default=False, default=None):
829
830 - def __get__(self, dbobj, owner="I don't know about this"):
831 if self.has_default and not self.isset(dbobj):
832 return self.default
833 else:
834 return wrapper.__get__(self, dbobj, owner)
835
867
870
872 """
873 This datatype will manage several columns of the same datatype
874 that follow a naming convention. It is maily indended for database
875 tables that store several versions of a string in several columns,
876 as for example for applications where you have more than one language.
877
878 Example::
879
880 CREATE TABLE item_category
881 (
882 id SERIAL,
883 name_en TEXT,
884 name_de TEXT,
885
886 PRIMARY KEY(id)
887 )
888
889 And in Python::
890
891 class item_category:
892 id = serial()
893 name = datatype_group(Unicode, ('en', 'de',), 'en')
894
895 The datatype_group instance will add one dbproperty to the dbclass for
896 each postfix you supply. These are not accessible directly, but the
897 datatype_group dbproperty will behave like a Python dictionary::
898
899 lang = 'de'
900 category_name = item_category.name[lang]
901
902 The naming convention for the database column goes
903 <attribute name>_<postfix>. If you want to use your own column names,
904 you may pass a dictionary as the postfixes parameter like::
905
906 { 'p1': sql.column(name1), 'p2': sql.column(name2) }
907
908 In this case the implementation will accept any datatype for the
909 postfixes.
910 """
911
912 - def __init__(self, inside_datatype, postfixes, default_postfix=None,
913 title=None, validators=(),
914 has_default=False):
915 """
916 The rest of the params just like L{datatype}.
917
918 @param inside_datatype: This is the datatype <b>class</b> for the
919 managed attributes.
920 @param postfixes: This may either be a tuple of strings, which will be
921 used in the column names as described above or a dictionary
922 mapping arbitrary keys to column names.
923 """
924 datatype.__init__(self, None, title, validators,
925 has_default)
926
927 self.inside_datatype = inside_datatype
928 self.inside_dbproperty_names = {}
929
930 self.postfixes = postfixes
931 self.default_postfix = default_postfix
932
933 for prop in self.__dict__.values():
934 if isinstance(prop, widget):
935 prop.__init_dbproperty__(self)
936
937
939 datatype.__init_dbclass__(self, dbclass, attribute_name)
940
941 if type(self.postfixes) != DictType:
942 p = {}
943 for postfix in self.postfixes:
944 if type(postfix) != StringType:
945 raise TypeError("All postfixes must be strings!")
946
947 if p.has_key(postfix):
948 raise ValueError("The members of the postfixes "
949 "parameter must be unique.")
950
951 p[postfix] = sql.column("%s_%s" % ( attribute_name,
952 postfix, ))
953
954 self.postfixes = p
955
956 else:
957 for postfix, column in self.postfixes:
958 if not isinstance(column, sql.column):
959 self.postfixes[postfix] = sql.column(column)
960
961
962 for postfix, column in self.postfixes.items():
963 attr_name = " %s_%s" % ( attribute_name, repr(postfix), )
964 dt = self.inside_datatype(column=column,
965 title=self.title,
966 validators=self.validators,
967 has_default=self.has_default)
968 setattr(dbclass, attr_name, dt)
969 dt.__init_dbclass__(dbclass, attr_name)
970 self.inside_dbproperty_names[postfix] = attr_name
971
972
974 """
975 Return a dict as { postfix: <datatype instance> }
976 """
977 ret = {}
978 for postfix, attr_name in self.inside_dbproperty_names.items():
979 ret[postfix] = self.dbclass.__dict__[attr_name]
980
981 return ret
982
983 - def __get__(self, dbobj, owner=""):
984 return self.result(self, dbobj)
985
987 if type(value) != DictType:
988 raise TypeError("%s.%s dbattribute can only be set to a dict!" % \
989 ( self.dbclass.__name__, self.attribute_name, ))
990
991 for postfix, v in value.items():
992 if postfix not in self.postfixes:
993 raise KeyError("%s not a valid postfix" % repr(postfix))
994
995 property = self.inside_dbproperties()[postfix]
996 property.__set__(dbobj, v)
997
999 raise NotImplementedError()
1000
1001 - def isset(self, dbobj):
1007
1009 return None
1010
1012 return False
1013
1015 return False
1016
1017
1019 - def __init__(self, parent_dbproperty, dbobj):
1020 self.parent = parent_dbproperty
1021 self.dbobj = dbobj
1022
1039
1046