Browse Source

More methods implementations in LeCrud

Implements update, delete, insert and delete_multi (not tested)
Yann Weber 9 years ago
parent
commit
9cbef1179b
3 changed files with 137 additions and 31 deletions
  1. 133
    27
      leapi/lecrud.py
  2. 0
    1
      leapi/leobject.py
  3. 4
    3
      leapi/letype.py

+ 133
- 27
leapi/lecrud.py View File

@@ -4,11 +4,20 @@
4 4
 # @brief This package contains the abstract class representing Lodel Editorial components
5 5
 #
6 6
 
7
+import warnings
7 8
 import importlib
8 9
 import re
9 10
 
11
+import leapi.leobject
10 12
 import EditorialModel.fieldtypes.generic
11 13
 
14
+## @brief When an error concern a query
15
+class LeApiQueryError(EditorialModel.fieldtypes.generic.FieldTypeDataCheckError): pass
16
+
17
+## @brief When an error concerns a datas
18
+class LeApiDataCheckError(EditorialModel.fieldtypes.generic.FieldTypeDataCheckError): pass
19
+    
20
+
12 21
 ## @brief Main class to handler lodel editorial components (relations and objects)
13 22
 class _LeCrud(object):
14 23
     ## @brief The datasource
@@ -38,7 +47,8 @@ class _LeCrud(object):
38 47
             return getattr(mod, name)
39 48
         except AttributeError:
40 49
             return False
41
-
50
+    
51
+    ## @return LeObject class
42 52
     @classmethod
43 53
     def leobject(cls):
44 54
         return cls.name2class('LeObject')
@@ -53,16 +63,49 @@ class _LeCrud(object):
53 63
     def fieldtypes_internal(self):
54 64
         return { fname: ft for fname, ft in cls.fieldtypes().items() if hasattr(ft, 'internal') and ft.internal }
55 65
     
66
+    ## @return A list of field name
67
+    @classmethod
68
+    def fieldlist(cls):
69
+        return cls.fieldtypes().keys()
70
+    
71
+    ## @brief Update a component in DB
72
+    # @param datas dict : If None use instance attributes to update de DB
73
+    # @return True if success
74
+    # @todo better error handling
75
+    def update(self, datas = None):
76
+        datas = self.datas(internal=False) if datas is None else datas
77
+        upd_datas = self.prepare_datas(datas, complete = False, allow_internal = False)
78
+        filters = [self._id_filter()]
79
+        rel_filters = []
80
+        ret = self._datasource.update(self.__class__, filters, rel_filters, upd_datas)
81
+        if ret == 1:
82
+            return True
83
+        else:
84
+            #ERROR HANDLING
85
+            return False
86
+    
87
+    ## @brief Delete a component
88
+    # @return True if success
89
+    # @todo better error handling
90
+    def delete(self):
91
+        filters = [self._id_filter()]
92
+        rel_filters = []
93
+        ret = self._datasource.delete(self.__class__, filters, rel_filters)
94
+        if ret == 1:
95
+            return True
96
+        else:
97
+            #ERROR HANDLING
98
+            return False
99
+
56 100
     ## @brief Check that datas are valid for this type
57 101
     # @param datas dict : key == field name value are field values
58 102
     # @param complete bool : if True expect that datas provide values for all non internal fields
59 103
     # @param allow_internal bool : if True don't raise an error if a field is internal
60
-    # @throw AttributeError if datas provides values for automatic fields
61
-    # @throw AttributeError if datas provides values for fields that doesn't exists
104
+    # @return Checked datas
105
+    # @throw LeObjectError if errors reported during check
62 106
     @classmethod
63 107
     def check_datas_value(cls, datas, complete = False, allow_internal = True):
64
-
65
-        err_l = []
108
+        err_l = [] #Stores errors
66 109
         excepted = set()
67 110
         internal = set() #Only used if not allow_internal
68 111
         if not allow_internal:
@@ -77,7 +120,6 @@ class _LeCrud(object):
77 120
             internal = set(internal)
78 121
         else:
79 122
             excepted = set( cls.fieldtypes().keys() )
80
-
81 123
         provided = set(datas.keys())
82 124
 
83 125
         #Searching unknown fields
@@ -94,7 +136,6 @@ class _LeCrud(object):
94 136
             wrong_internal = provided & internal
95 137
             for wint in wrong_internal:
96 138
                 err_l.append(AttributeError("A value was provided for the field '%s' but it is marked as internal"%wint))
97
-        
98 139
         #Datas check
99 140
         checked_datas = dict()
100 141
         for name, value in [ (name, value) for name, value in datas.items() if name in (excepted | internal) ]:
@@ -104,13 +145,8 @@ class _LeCrud(object):
104 145
 
105 146
         if len(missing_data) > 0:
106 147
             raise LeObjectError("The argument complete was True but some fields are missing : %s" % (missing_data))
107
-
108 148
         return checked_datas
109 149
 
110
-    @classmethod
111
-    def fieldlist(cls):
112
-        return cls.fieldtypes().keys()
113
-    
114 150
     ## @brief Retrieve a collection of lodel editorial components
115 151
     #
116 152
     # @param query_filters list : list of string of query filters (or tuple (FIELD, OPERATOR, VALUE) ) see @ref leobject_filters
@@ -135,7 +171,84 @@ class _LeCrud(object):
135 171
         db_datas = cls._datasource.get(cls, filters, relational_filters)
136 172
 
137 173
         return [ cls(**datas) for datas in db_datas]
138
-            
174
+    
175
+    ## @brief Given filters delete components
176
+    # @param filters list : list of string of query filters (or tuple (FIELD, OPERATOR, VALUE) ) see @ref leobject_filters
177
+    # @return the number of deleted components
178
+    # @todo Check for Abstract calls (with cls == LeCrud)
179
+    @classmethod
180
+    def delete_multi(cls, filters):
181
+        filters, rel_filters = cls._prepare_filters(filters)
182
+        return cls._datasource.delete(cls, filters, rel_filters)
183
+    
184
+    ## @brief Insert a new component
185
+    # @param datas dict : The value of object we want to insert
186
+    # @return A new id if success else False
187
+    @classmethod
188
+    def insert(cls, datas):
189
+        insert_datas = self.prepare_datas(datas, complete = True, allow_internal = False)
190
+        return cls._datasource.insert(cls, insert_datas)
191
+    
192
+    ## @brief Check and prepare datas
193
+    # 
194
+    # @warning when complete = False we are not able to make construct_datas() and _check_data_consistency()
195
+    # 
196
+    # @param datas dict : {fieldname : fieldvalue, ...}
197
+    # @param complete bool : If True you MUST give all the datas
198
+    # @param allow_internal : Wether or not interal fields are expected in datas
199
+    # @return Datas ready for use
200
+    @classmethod
201
+    def prepare_datas(cls, datas, complete = False, allow_internal = True):
202
+        if not complete:
203
+            warnings.warn("Actual implementation can make datas construction and consitency checks fails when datas are not complete")
204
+        ret_dats = self.check_datas_value(cls, datas, complete, allow_internal)
205
+        ret_datas = self._construct_datas(cls, ret_datas)
206
+        ret_datas = self._check_data_consistency(cls, ret_datas)
207
+        return ret_datas
208
+
209
+    #-###################-#
210
+    #   Private methods   #
211
+    #-###################-#
212
+    
213
+    ## @brief Build a filter to select an object with a specific ID
214
+    # @warning assert that the uid is not composed with multiple fieldtypes
215
+    # @return A filter of the form tuple(UID, '=', self.UID)
216
+    def _id_filter(self):
217
+        id_name = self._uid_fieldtype.keys()[0]
218
+        return ( id_name, '=', getattr(self, id_name) )
219
+
220
+    ## @brief Construct datas values
221
+    #
222
+    # @warning assert that datas is complete
223
+    #
224
+    # @param datas dict : Datas that have been returned by LeCrud.check_datas_value() methods
225
+    # @return A new dict of datas
226
+    # @todo Decide wether or not the datas are modifed inplace or returned in a new dict (second solution for the moment)
227
+    @classmethod
228
+    def _construct_datas(cls, datas):
229
+        res_datas = dict()
230
+        for fname, ftype in cls.fieldtypes().items():
231
+            if fname in datas:
232
+                res_datas[fname] = ftype.construct_data(datas)
233
+        return res_datas
234
+    ## @brief Check datas consistency
235
+    # 
236
+    # @warning assert that datas is complete
237
+    #
238
+    # @param datas dict : Datas that have been returned by LeCrud._construct_datas() method
239
+    # @throw LeApiDataCheckError if fails
240
+    @classmethod
241
+    def _check_datas_consistency(cls, datas):
242
+        err_l = []
243
+        for fname, ftype in cls.fieldtypes().items():
244
+            ret = ftype.check_data_consistency(datas)
245
+            if isinstance(ret, Exception):
246
+                err_l.append(ret)
247
+
248
+        if len(err_l) > 0:
249
+            raise LeApiDataCheckError("Datas consistency checks fails", err_l)
250
+        
251
+
139 252
     ## @brief Prepare a field_list
140 253
     # @param field_list list : List of string representing fields
141 254
     # @return A well formated field list
@@ -174,13 +287,6 @@ class _LeCrud(object):
174 287
             return ValueError("No such field '%s' in %s"%(field, cls.__name__))
175 288
         return field
176 289
 
177
-    ## @brief Check if a field is relational or not
178
-    # @param field str : the field to test
179
-    # @return True if the field is relational else False
180
-    @staticmethod
181
-    def _field_is_relational(field):
182
-        return field.startswith('superior.') or field.startswith('subordinate')
183
-
184 290
     ## @brief Prepare filters for datasource
185 291
     # 
186 292
     # This method divide filters in two categories :
@@ -193,7 +299,6 @@ class _LeCrud(object):
193 299
     # @return a tuple(FILTERS, RELATIONNAL_FILTERS
194 300
     #
195 301
     # @see @ref datasource_side
196
-
197 302
     @classmethod
198 303
     def _prepare_filters(cls, filters_l):
199 304
         filters = list()
@@ -258,11 +363,12 @@ class _LeCrud(object):
258 363
         cls._query_re = re.compile('^\s*(?P<field>(((superior)|(subordinate))\.)?[a-z_][a-z0-9\-_]*)\s*'+op_re_piece+'\s*(?P<value>[^<>=!].*)\s*$', flags=re.IGNORECASE)
259 364
         pass
260 365
     
366
+    ## @brief Check if a field is relational or not
367
+    # @param field str : the field to test
368
+    # @return True if the field is relational else False
369
+    @staticmethod
370
+    def _field_is_relational(field):
371
+        return field.startswith('superior.') or field.startswith('subordinate')
372
+
261 373
 
262
-            
263
-class LeApiQueryError(EditorialModel.fieldtypes.generic.FieldTypeDataCheckError):
264
-    pass
265 374
 
266
-class LeApiDataCheckError(EditorialModel.fieldtypes.generic.FieldTypeDataCheckError):
267
-    pass
268
-    

+ 0
- 1
leapi/leobject.py View File

@@ -15,7 +15,6 @@ import warnings
15 15
 
16 16
 import leapi
17 17
 import EditorialModel
18
-from leapi.lecrud import _LeCrud
19 18
 from leapi.lefactory import LeFactory
20 19
 from EditorialModel.types import EmType
21 20
 

+ 4
- 3
leapi/letype.py View File

@@ -204,7 +204,8 @@ class LeType(object):
204 204
 
205 205
     @classmethod
206 206
     def prepare_datas(cls, datas, complete = False, allow_internal = True):
207
-        self.check_data_value(cls, datas, complete, allow_internal)
208
-        self.construct_data(cls, datas)
209
-        self.check_data_consistency(cls, datas)
207
+        ret_dats = self.check_datas_value(cls, datas, complete, allow_internal)
208
+        ret_datas = self.construct_data(cls, ret_datas)
209
+        ret_datas = self.check_data_consistency(cls, ret_datas)
210
+        return ret_datas
210 211
 

Loading…
Cancel
Save