|
@@ -45,7 +45,7 @@ class LeQuery(object):
|
45
|
45
|
raise NotImplementedError("Abstract class")
|
46
|
46
|
if not issubclass(target_class, LeObject):
|
47
|
47
|
raise TypeError("target class has to be a child class of LeObject")
|
48
|
|
- self.__target_class = target_class
|
|
48
|
+ self._target_class = target_class
|
49
|
49
|
|
50
|
50
|
##@brief Execute a query and return the result
|
51
|
51
|
# @param **datas
|
|
@@ -56,18 +56,18 @@ class LeQuery(object):
|
56
|
56
|
#elsewhere
|
57
|
57
|
def execute(self, datasource, datas = None):
|
58
|
58
|
if len(datas) > 0:
|
59
|
|
- self.__target_class.check_datas_value(
|
|
59
|
+ self._target_class.check_datas_value(
|
60
|
60
|
datas,
|
61
|
61
|
**self._data_check_args)
|
62
|
|
- self.__target_class.prepare_datas() #not yet implemented
|
|
62
|
+ self._target_class.prepare_datas() #not yet implemented
|
63
|
63
|
if self._hook_prefix is None:
|
64
|
64
|
raise NotImplementedError("Abstract method")
|
65
|
65
|
LodelHook.call_hook( self._hook_prefix+'_pre',
|
66
|
|
- self.__target_class,
|
|
66
|
+ self._target_class,
|
67
|
67
|
datas)
|
68
|
68
|
ret = self.__query(datasource, **datas)
|
69
|
69
|
ret = LodelHook.call_hook( self._hook_prefix+'_post',
|
70
|
|
- self.__target_class,
|
|
70
|
+ self._target_class,
|
71
|
71
|
ret)
|
72
|
72
|
return ret
|
73
|
73
|
|
|
@@ -76,11 +76,22 @@ class LeQuery(object):
|
76
|
76
|
# @return query result
|
77
|
77
|
def __query(self, **datas):
|
78
|
78
|
raise NotImplementedError("Asbtract method")
|
|
79
|
+
|
|
80
|
+ ##@return a dict with query infos
|
|
81
|
+ def dump_infos(self):
|
|
82
|
+ return {'target_class': self._target_class}
|
|
83
|
+
|
|
84
|
+ def __repr__(self):
|
|
85
|
+ ret = "<{classname} target={target_class}>"
|
|
86
|
+ return ret.format(
|
|
87
|
+ classname=self.__class__.__name__,
|
|
88
|
+ target_class = self._target_class)
|
|
89
|
+
|
79
|
90
|
|
80
|
91
|
class LeFilteredQuery(LeQuery):
|
81
|
92
|
|
82
|
93
|
##@brief The available operators used in query definitions
|
83
|
|
- query_operators = [
|
|
94
|
+ _query_operators = [
|
84
|
95
|
'=',
|
85
|
96
|
'<=',
|
86
|
97
|
'>=',
|
|
@@ -91,6 +102,9 @@ class LeFilteredQuery(LeQuery):
|
91
|
102
|
'not in',
|
92
|
103
|
'like',
|
93
|
104
|
'not like']
|
|
105
|
+
|
|
106
|
+ ##@brief Regular expression to process filters
|
|
107
|
+ _query_re = None
|
94
|
108
|
|
95
|
109
|
##@brief Abtract constructor for queries with filter
|
96
|
110
|
# @param target_class LeObject : class of object the query is about
|
|
@@ -99,16 +113,29 @@ class LeFilteredQuery(LeQuery):
|
99
|
113
|
# For tuple (FIELD,OPERATOR,VALUE)
|
100
|
114
|
def __init__(self, target_class, query_filters = None):
|
101
|
115
|
super().__init__(target_class)
|
102
|
|
- ##@brief The query filter
|
|
116
|
+ ##@brief The query filter tuple(std_filter, relational_filters)
|
103
|
117
|
self.__query_filter = None
|
104
|
|
- self.set_query_filter(query_filter)
|
|
118
|
+ self.set_query_filter(query_filters)
|
105
|
119
|
|
106
|
120
|
##@brief Add filter(s) to the query
|
107
|
121
|
#@param query_filter list|tuple|str : A single filter or a list of filters
|
108
|
122
|
#@see LeFilteredQuery._prepare_filters()
|
109
|
|
- def filter(self, query_filter):
|
110
|
|
- pass
|
|
123
|
+ def set_query_filter(self, query_filter):
|
|
124
|
+ if isinstance(query_filter, str):
|
|
125
|
+ query_filter = [query_filter]
|
|
126
|
+ self.__query_filter = self._prepare_filters(query_filter)
|
|
127
|
+
|
|
128
|
+ def dump_infos(self):
|
|
129
|
+ ret = super().dump_infos()
|
|
130
|
+ ret['query_filter'] = self.__query_filter
|
|
131
|
+ return ret
|
111
|
132
|
|
|
133
|
+ def __repr__(self):
|
|
134
|
+ ret = "<{classname} target={target_class} query_filter={query_filter}>"
|
|
135
|
+ return ret.format(
|
|
136
|
+ classname=self.__class__.__name__,
|
|
137
|
+ query_filter = self.__query_filter,
|
|
138
|
+ target_class = self._target_class)
|
112
|
139
|
## @brief Prepare filters for datasource
|
113
|
140
|
#
|
114
|
141
|
#A filter can be a string or a tuple with len = 3.
|
|
@@ -140,18 +167,20 @@ class LeFilteredQuery(LeQuery):
|
140
|
167
|
#@param filters_l list : This list of str or tuple (or both)
|
141
|
168
|
#@return a tuple(FILTERS, RELATIONNAL_FILTERS
|
142
|
169
|
#@todo move this doc in another place (a dedicated page ?)
|
143
|
|
- @classmethod
|
144
|
|
- def _prepare_filters(cls, filters_l):
|
|
170
|
+ def _prepare_filters(self, filters_l):
|
145
|
171
|
filters = list()
|
146
|
172
|
res_filters = list()
|
147
|
173
|
rel_filters = list()
|
148
|
174
|
err_l = dict()
|
149
|
175
|
#Splitting in tuple if necessary
|
150
|
|
- for fil in filters_l:
|
|
176
|
+ for i,fil in enumerate(filters_l):
|
151
|
177
|
if len(fil) == 3 and not isinstance(fil, str):
|
152
|
178
|
filters.append(tuple(fil))
|
153
|
179
|
else:
|
154
|
|
- filters.append(cls.split_filter(fil))
|
|
180
|
+ try:
|
|
181
|
+ filters.append(self.split_filter(fil))
|
|
182
|
+ except ValueError as e:
|
|
183
|
+ err_l["filter %d" % i] = e
|
155
|
184
|
|
156
|
185
|
for field, operator, value in filters:
|
157
|
186
|
# Spliting field name to be able to detect a relational field
|
|
@@ -165,7 +194,7 @@ class LeFilteredQuery(LeQuery):
|
165
|
194
|
field name" % fieldname)
|
166
|
195
|
continue
|
167
|
196
|
# Checking field against target_class
|
168
|
|
- ret = self.__check_field(self.__target_class, field)
|
|
197
|
+ ret = self.__check_field(self._target_class, field)
|
169
|
198
|
if isinstance(ret, Exception):
|
170
|
199
|
err_l[field] = ret
|
171
|
200
|
continue
|
|
@@ -178,8 +207,8 @@ a relational field, but %s.%s was present in the filter"
|
178
|
207
|
field,
|
179
|
208
|
ref_field))
|
180
|
209
|
# Prepare relational field
|
181
|
|
- if cls.field(field).is_reference():
|
182
|
|
- ret = cls._prepare_relational_fields(field, ref_field)
|
|
210
|
+ if self._target_class.field(field).is_reference():
|
|
211
|
+ ret = self._prepare_relational_fields(field, ref_field)
|
183
|
212
|
if isinstance(ret, Exception):
|
184
|
213
|
err_l[field] = ret
|
185
|
214
|
else:
|
|
@@ -267,7 +296,7 @@ a relational field, but %s.%s was present in the filter"
|
267
|
296
|
#@return a well formed relational filter tuple or an Exception instance
|
268
|
297
|
@classmethod
|
269
|
298
|
def __prepare_relational_fields(cls, fieldname, ref_field = None):
|
270
|
|
- datahandler = self.__target_class.field(fieldname)
|
|
299
|
+ datahandler = self._target_class.field(fieldname)
|
271
|
300
|
# now we are going to fetch the referenced class to see if the
|
272
|
301
|
# reference field is valid
|
273
|
302
|
ref_classes = datahandler.linked_classes
|
|
@@ -300,14 +329,14 @@ class LeInsertQuery(LeQuery):
|
300
|
329
|
## @brief Implements an insert query operation, with only one insertion
|
301
|
330
|
# @param **datas : datas to be inserted
|
302
|
331
|
def __query(self, datasource, **datas):
|
303
|
|
- nb_inserted = datasource.insert(self.__target_class,**datas)
|
|
332
|
+ nb_inserted = datasource.insert(self._target_class,**datas)
|
304
|
333
|
if nb_inserted < 0:
|
305
|
334
|
raise LeQueryError("Insertion error")
|
306
|
335
|
return nb_inserted
|
307
|
336
|
## @brief Implements an insert query operation, with multiple insertions
|
308
|
337
|
# @param datas : list of **datas to be inserted
|
309
|
338
|
def __query(self, datasource, datas):
|
310
|
|
- nb_inserted = datasource.insert_multi(self.__target_class,datas_list)
|
|
339
|
+ nb_inserted = datasource.insert_multi(self._target_class,datas_list)
|
311
|
340
|
if nb_inserted < 0:
|
312
|
341
|
raise LeQueryError("Multiple insertions error")
|
313
|
342
|
return nb_inserted
|
|
@@ -330,9 +359,9 @@ class LeUpdateQuery(LeFilteredQuery):
|
330
|
359
|
# @exception when the number of updated items is not as expected
|
331
|
360
|
def __query(self, datasource, **datas):
|
332
|
361
|
# select _uid corresponding to query_filter
|
333
|
|
- l_uids=datasource.select(self.__target_class,list(self.__target_class.getuid()),query_filter,None, None, None, None, 0, False)
|
|
362
|
+ l_uids=datasource.select(self._target_class,list(self._target_class.getuid()),query_filter,None, None, None, None, 0, False)
|
334
|
363
|
# list of dict l_uids : _uid(s) of the objects to be updated, corresponding datas
|
335
|
|
- nb_updated = datasource.update(self.__target_class,l_uids, **datas)
|
|
364
|
+ nb_updated = datasource.update(self._target_class,l_uids, **datas)
|
336
|
365
|
if nb_updated != len(l_uids):
|
337
|
366
|
raise LeQueryError("Number of updated items: %d is not as expected: %d " % (nb_updated, len(l_uids)))
|
338
|
367
|
return nb_updated
|
|
@@ -358,9 +387,9 @@ class LeDeleteQuery(LeFilteredQuery):
|
358
|
387
|
# @exception when the number of deleted items is not as expected
|
359
|
388
|
def __query(self, datasource):
|
360
|
389
|
# select _uid corresponding to query_filter
|
361
|
|
- l_uids=datasource.select(self.__target_class,list(self.__target_class.getuid()),query_filter,None, None, None, None, 0, False)
|
|
390
|
+ l_uids=datasource.select(self._target_class,list(self._target_class.getuid()),query_filter,None, None, None, None, 0, False)
|
362
|
391
|
# list of dict l_uids : _uid(s) of the objects to be deleted
|
363
|
|
- nb_deleted = datasource.update(self.__target_class,l_uids, **datas)
|
|
392
|
+ nb_deleted = datasource.update(self._target_class,l_uids, **datas)
|
364
|
393
|
if nb_deleted != len(l_uids):
|
365
|
394
|
raise LeQueryError("Number of deleted items %d is not as expected %d " % (nb_deleted, len(l_uids)))
|
366
|
395
|
return nb_deleted
|
|
@@ -399,10 +428,10 @@ class LeGetQuery(LeFilteredQuery):
|
399
|
428
|
|
400
|
429
|
if 'field_list' not in kwargs:
|
401
|
430
|
#field_list = target_class.get_field_list
|
402
|
|
- field_list = target_class.fieldnames()
|
|
431
|
+ self.__field_list = target_class.fieldnames(include_ro = True)
|
403
|
432
|
else:
|
404
|
433
|
#target_class.check_fields(kwargs['field_list'])
|
405
|
|
- field_list = kwargs['field_list']
|
|
434
|
+ self.__field_list = kwargs['field_list']
|
406
|
435
|
if 'order' in kwargs:
|
407
|
436
|
#check kwargs['order']
|
408
|
437
|
self.__order = kwargs['order']
|
|
@@ -430,11 +459,23 @@ class LeGetQuery(LeFilteredQuery):
|
430
|
459
|
|
431
|
460
|
##@brief Implements select query operations
|
432
|
461
|
# @returns a list containing the item(s)
|
433
|
|
-
|
434
|
462
|
def __query(self, datasource):
|
435
|
463
|
# select datas corresponding to query_filter
|
436
|
|
- l_datas=datasource.select(self.__target_class,list(self.field_list),self.query_filter,None, self.__order, self.__group, self.__limit, self.offset, False)
|
|
464
|
+ l_datas=datasource.select(self._target_class,list(self.field_list),self.query_filter,None, self.__order, self.__group, self.__limit, self.offset, False)
|
437
|
465
|
return l_datas
|
438
|
|
-
|
|
466
|
+
|
|
467
|
+ ##@return a dict with query infos
|
|
468
|
+ def dump_infos(self):
|
|
469
|
+ ret = super().dump_infos()
|
|
470
|
+ ret.update( { 'field_list' : self.__field_list,
|
|
471
|
+ 'order' : self.__order,
|
|
472
|
+ 'group' : self.__group,
|
|
473
|
+ 'limit' : self.__limit,
|
|
474
|
+ 'offset': self.__offset,
|
|
475
|
+ })
|
|
476
|
+ return ret
|
439
|
477
|
|
|
478
|
+ def __repr__(self):
|
|
479
|
+ ret = "<LeGetQuery target={target_class} filter={query_filter} field_list={field_list} order={order} group={group} limit={limit} offset={offset}>"
|
|
480
|
+ return ret.format(**self.dump_infos())
|
440
|
481
|
|