|
@@ -3,80 +3,44 @@
|
3
|
3
|
import types
|
4
|
4
|
import importlib
|
5
|
5
|
|
6
|
|
-
|
7
|
|
-## @brief Abstract class representing a fieldtype
|
8
|
|
-#
|
9
|
|
-# Important notes on datas checking :
|
10
|
6
|
class GenericFieldType(object):
|
|
7
|
+
|
|
8
|
+ help_text = 'Generic field type : abstract class for every fieldtype'
|
11
|
9
|
|
12
|
|
- ## @brief Text describing the fieldtype
|
13
|
|
- help = 'Generic field type : abstract class for every fieldtype'
|
14
|
|
- ## @brief Allowed type for handled datas
|
15
|
|
- _allowed_ftype = ['char', 'str', 'int', 'bool', 'datetime', 'text', 'rel2type', 'leobject']
|
16
|
|
-
|
17
|
|
- ## @brief The basic lowlevel value type
|
18
|
|
- ftype = None
|
19
|
|
-
|
20
|
|
- ## @brief Instanciate a new fieldtype
|
21
|
|
- # @param ftype str : The type of datas handled by this fieldtype
|
22
|
|
- # @param nullable bool : is None allowed as value ?
|
23
|
|
- # @param check_function function : A callback check function that takes 1 argument and raise a TypeError if the validation fails
|
24
|
|
- # @param uniq bool : Indicate if a field should handle uniq values
|
25
|
|
- # @param primary bool : If true the field is a primary key
|
26
|
|
- # @param **kwargs dict : Other arguments
|
27
|
|
- # @throw NotImplementedError if called directly
|
28
|
|
- # @throw AttributeError if bad ftype
|
29
|
|
- # @throw AttributeError if bad check_function
|
30
|
|
- def __init__(self, ftype, nullable=True, check_data_value=None, uniq=False, primary=False, **kwargs):
|
|
10
|
+ ## @param internal False | str : define wheter or not a field is internal
|
|
11
|
+ # @throw NotImplementedError if called from bad class
|
|
12
|
+ def __init__(self, internal = False, **args):
|
31
|
13
|
if self.__class__ == GenericFieldType:
|
32
|
14
|
raise NotImplementedError("Abstract class")
|
33
|
|
-
|
34
|
|
- if self.ftype is None:
|
35
|
|
- raise RuntimeError("The class attribute ftype is not properly set by the %s EmFieldType" % self.name)
|
36
|
|
-
|
37
|
|
- if ftype not in self._allowed_ftype:
|
38
|
|
- raise AttributeError("Ftype '%s' not known" % ftype)
|
39
|
|
-
|
40
|
|
- if ftype != self.__class__.ftype:
|
41
|
|
- raise RuntimeError("The ftype is not the same for the instance and the class. Maybe %s reimplement ftype at class level but shouldn't" % self.name)
|
42
|
|
-
|
43
|
|
- self.ftype = ftype
|
44
|
|
- self.nullable = bool(nullable)
|
45
|
|
- self.uniq = bool(uniq)
|
46
|
|
-
|
47
|
|
- if 'default' in kwargs:
|
48
|
|
- self.default, error = self.check_data_value(kwargs['default'])
|
49
|
|
- if error:
|
50
|
|
- raise error
|
51
|
|
- del kwargs['default']
|
52
|
|
-
|
53
|
|
- for argname, argvalue in kwargs.items():
|
54
|
|
- setattr(self, argname, argvalue)
|
55
|
|
-
|
56
|
|
- ## @return A fieldtype name from an instance
|
|
15
|
+ self.internal = internal #Check this value ?
|
|
16
|
+
|
|
17
|
+ for argname, argval in args.items():
|
|
18
|
+ setattr(self, argname, argval)
|
|
19
|
+
|
|
20
|
+ ## Fieldtype name
|
|
21
|
+ # @todo should be a staticmethod
|
57
|
22
|
@property
|
58
|
23
|
def name(self):
|
59
|
24
|
return self.__module__.split('.')[-1]
|
60
|
25
|
|
61
|
|
- ## @return True if a field is internal, else returns False
|
|
26
|
+ ## @return True if a fieldtype is internal
|
62
|
27
|
def is_internal(self):
|
63
|
|
- return hasattr(self, 'internal') and self.internal
|
64
|
|
-
|
65
|
|
- ## @brief Check if a Value is correct else return a check fail reason
|
66
|
|
- # @param value * : The value to check
|
67
|
|
- # @return (checked_and_casted_value, Exception|None)
|
|
28
|
+ return self.internal != False
|
|
29
|
+
|
|
30
|
+ ## @brief Take care to call the fieldtype defined _check_data_value() method
|
|
31
|
+ # @return a tuple (value, error|None)
|
68
|
32
|
def check_data_value(self, value):
|
69
|
|
- if value is None:
|
70
|
|
- if not self.nullable:
|
71
|
|
- return (None, TypeError("'None' value but field is not nullable"))
|
72
|
|
- return (None, None)
|
73
|
33
|
return self._check_data_value(value)
|
|
34
|
+
|
|
35
|
+ def _check_data_value(self, value):
|
|
36
|
+ return (value, None)
|
74
|
37
|
|
75
|
|
- ## @brief Build automatic fields values
|
|
38
|
+ ## @brief Build field value
|
76
|
39
|
# @param lec LeCrud : A LeCrud child class
|
77
|
|
- # @param fname str : The field name
|
78
|
|
- # @param datas dict : dict storing fields values
|
79
|
|
- # @return constructed datas
|
|
40
|
+ # @param fname str : The field name
|
|
41
|
+ # @param datas dict : dict storing fields values (from the lec)
|
|
42
|
+ # @return constructed datas (for the fname field)
|
|
43
|
+ # @throw RuntimeError if unable to construct data
|
80
|
44
|
def construct_data(self, lec, fname, datas):
|
81
|
45
|
if fname in datas:
|
82
|
46
|
return datas[fname]
|
|
@@ -85,7 +49,7 @@ class GenericFieldType(object):
|
85
|
49
|
elif lec.fieldtypes()[fname].nullable:
|
86
|
50
|
return None
|
87
|
51
|
raise RuntimeError("Unable to construct data for field %s", fname)
|
88
|
|
-
|
|
52
|
+
|
89
|
53
|
## @brief Check datas consistency
|
90
|
54
|
# @param leo LeCrud : A LeCrud child class instance
|
91
|
55
|
# @param fname str : The field name
|
|
@@ -94,12 +58,6 @@ class GenericFieldType(object):
|
94
|
58
|
def check_data_consistency(self, lec, fname, datas):
|
95
|
59
|
return True
|
96
|
60
|
|
97
|
|
- ## @brief Dummy check, designed to be implemented in child classes
|
98
|
|
- # @param value * : The value
|
99
|
|
- # @return (checked_and_casted_value, Exception|None)
|
100
|
|
- def _check_data_value(self, value):
|
101
|
|
- return (value, None)
|
102
|
|
-
|
103
|
61
|
## @brief Given a fieldtype name return the associated python class
|
104
|
62
|
# @param fieldtype_name str : A fieldtype name
|
105
|
63
|
# @return An GenericFieldType derivated class
|
|
@@ -122,18 +80,83 @@ class GenericFieldType(object):
|
122
|
80
|
hash_dats.append((kdic, getattr(self, kdic)))
|
123
|
81
|
return hash(tuple(hash_dats))
|
124
|
82
|
|
125
|
|
- ## @brief Transform a value into a valid python representation according to the fieldtype
|
126
|
|
- # @param value ? : The value to cast
|
127
|
|
- # @param kwargs dict : optionnal cast arguments
|
128
|
|
- # @return Something (depending on the fieldtype
|
129
|
|
- # @throw AttributeError if error in argument given to the method
|
130
|
|
- # @throw TypeError if the cast is not possible
|
131
|
|
- def cast(self, value, **kwargs):
|
132
|
|
- if len(kwargs) > 0:
|
133
|
|
- raise AttributeError("No optionnal argument allowed for %s cast method" % self.__class__.__name__)
|
134
|
|
- return value
|
|
83
|
+class SingleValueFieldType(GenericFieldType):
|
|
84
|
+
|
|
85
|
+ ## @brief Instanciate a new fieldtype
|
|
86
|
+ # @param nullable bool : is None allowed as value ?
|
|
87
|
+ # @param uniqu bool : Indicate if a field should handle uniq value
|
|
88
|
+ # @param primary bool : If True the field is a primary key
|
|
89
|
+ # @param **args : Other arguments
|
|
90
|
+ # @throw NotImplementedError if called from bad class
|
|
91
|
+ def __init__(self, internal = False, nullable = True, uniq = False, primary = False, **args):
|
|
92
|
+ if self.__class__ == SingleValueFieldType:
|
|
93
|
+ raise NotImplementedError("Asbtract class")
|
|
94
|
+
|
|
95
|
+ super().__init__(internal, **args)
|
|
96
|
+
|
|
97
|
+ self.nullable = nullable
|
|
98
|
+ self.uniq = uniq
|
|
99
|
+ self.primary = primary
|
|
100
|
+ if 'default' in args:
|
|
101
|
+ self.default, error = self.check_data_value(args['default'])
|
|
102
|
+ if error:
|
|
103
|
+ raise error
|
|
104
|
+ del(args['default'])
|
135
|
105
|
|
|
106
|
+ def check_data_value(self, value):
|
|
107
|
+ if value is None:
|
|
108
|
+ if not self.nullable:
|
|
109
|
+ return (None, TypeError("'None' value but field is not nullable"))
|
|
110
|
+ return (None, None)
|
|
111
|
+ return super().check_data_value(value)
|
|
112
|
+
|
|
113
|
+class ReferenceFieldType(SingleValueFieldType):
|
136
|
114
|
|
|
115
|
+ ## @brief Instanciate a new fieldtype
|
|
116
|
+ #
|
|
117
|
+ #
|
|
118
|
+ # @param reference str : A string that defines the reference (can be 'object' or 'relation')
|
|
119
|
+ # @param nullable bool : is None allowed as value ?
|
|
120
|
+ # @param unique bool : Indicate if a field should handle uniq value
|
|
121
|
+ # @param primary bool : If True the field is a primary key
|
|
122
|
+ # @param **args : Other arguments
|
|
123
|
+ # @throw NotImplementedError if called from bad class
|
|
124
|
+ def __init__(self, reference, internal=False, nullable = True, uniq = False, primary = False, **args):
|
|
125
|
+ if reference.lower() not in ['relation', 'object']:
|
|
126
|
+ raise ValueError("Bad value for reference : %s. Excepted on of 'relation', 'object'" % reference)
|
|
127
|
+ self.reference = reference.lower()
|
|
128
|
+ super().__init__(
|
|
129
|
+ internal = internal,
|
|
130
|
+ nullable = nullable,
|
|
131
|
+ uniq = uniq,
|
|
132
|
+ primary = primary,
|
|
133
|
+ **args
|
|
134
|
+ );
|
|
135
|
+ pass
|
|
136
|
+
|
|
137
|
+class MultiValueFieldType(GenericFieldType):
|
|
138
|
+
|
|
139
|
+ ## @brief Instanciate a new multivalue fieldtype
|
|
140
|
+ #
|
|
141
|
+ # This fieldtype describe a field that handles multiple values referenced by a key (like a dict).
|
|
142
|
+ # A typicall example is the i18n fieldtype
|
|
143
|
+ # @param keyname str : The identifier key name
|
|
144
|
+ # @param key_fieldtype SingleValueFieldType : A SingleValueFieldType child class instance
|
|
145
|
+ # @param value_fieldtype SingleValueFieldType : A SingleValueFieldType child class instance
|
|
146
|
+ def __init__(self, internal, keyname, key_fieldtype, value_fieldtype, **args):
|
|
147
|
+ super().__init__(internal)
|
|
148
|
+ ## stores the keyname
|
|
149
|
+ self.keyname = keyname
|
|
150
|
+ ## stores the fieldtype that handles the key
|
|
151
|
+ self.key_fieldtype = key_fieldtype
|
|
152
|
+ ## stores the fieldtype that handles the values
|
|
153
|
+ self.value_fieldtype = value_fieldtype
|
|
154
|
+
|
|
155
|
+#
|
|
156
|
+#
|
|
157
|
+# Exceptions
|
|
158
|
+#
|
|
159
|
+#
|
137
|
160
|
class FieldTypeError(Exception):
|
138
|
161
|
pass
|
139
|
162
|
|