|
@@ -2,36 +2,12 @@
|
2
|
2
|
|
3
|
3
|
import re
|
4
|
4
|
import copy
|
|
5
|
+import inspect
|
5
|
6
|
|
6
|
|
-from .leobject import LeObject, LeApiErrors, LeApiDataCheckError
|
|
7
|
+from .exceptions import *
|
7
|
8
|
from lodel.plugin.hooks import LodelHook
|
8
|
9
|
from lodel import logger
|
9
|
10
|
|
10
|
|
-class LeQueryError(Exception):
|
11
|
|
- ##@brief Instanciate a new exceptions handling multiple exceptions
|
12
|
|
- #@param msg str : Exception message
|
13
|
|
- #@param exceptions dict : A list of data check Exception with concerned
|
14
|
|
- # field (or stuff) as key
|
15
|
|
- def __init__(self, msg = "Unknow error", exceptions = None):
|
16
|
|
- self._msg = msg
|
17
|
|
- self._exceptions = dict() if exceptions is None else exceptions
|
18
|
|
-
|
19
|
|
- def __repr__(self):
|
20
|
|
- return self.__str__()
|
21
|
|
-
|
22
|
|
- def __str__(self):
|
23
|
|
- msg = self._msg
|
24
|
|
- if isinstance(self._exceptions, dict):
|
25
|
|
- for_iter = self._exceptions.items()
|
26
|
|
- else:
|
27
|
|
- for_iter = enumerate(self.__exceptions)
|
28
|
|
- for obj, expt in for_iter:
|
29
|
|
- msg += "\n\t{expt_obj} : ({expt_name}) {expt_msg}; ".format(
|
30
|
|
- expt_obj = obj,
|
31
|
|
- expt_name=expt.__class__.__name__,
|
32
|
|
- expt_msg=str(expt)
|
33
|
|
- )
|
34
|
|
- return msg
|
35
|
11
|
|
36
|
12
|
##@todo check datas when running query
|
37
|
13
|
class LeQuery(object):
|
|
@@ -44,6 +20,7 @@ class LeQuery(object):
|
44
|
20
|
##@brief Abstract constructor
|
45
|
21
|
# @param target_class LeObject : class of object the query is about
|
46
|
22
|
def __init__(self, target_class):
|
|
23
|
+ from .leobject import LeObject
|
47
|
24
|
if self._hook_prefix is None:
|
48
|
25
|
raise NotImplementedError("Abstract class")
|
49
|
26
|
if not issubclass(target_class, LeObject):
|
|
@@ -504,41 +481,81 @@ class LeInsertQuery(LeQuery):
|
504
|
481
|
|
505
|
482
|
|
506
|
483
|
##@brief A query to update datas for a given object
|
|
484
|
+#
|
|
485
|
+#@todo Change behavior, Huge optimization problem when updating using filters
|
|
486
|
+#and not instance. We have to run a GET and then 1 update by fecthed object...
|
507
|
487
|
class LeUpdateQuery(LeFilteredQuery):
|
508
|
488
|
|
509
|
489
|
_hook_prefix = 'leapi_update_'
|
510
|
490
|
_data_check_args = { 'complete': True, 'allow_internal': False }
|
511
|
|
-
|
512
|
|
- def __init__(self, target_class, query_filter):
|
513
|
|
- super().__init__(target_class, query_filter)
|
514
|
491
|
|
515
|
|
- ##@brief Called by __query to do severals checks before running the update
|
516
|
|
- #@warning Optimization issue : each time this method is called we do
|
517
|
|
- #a LeGetQuery to fetch ALL datas and construct instances for being able to
|
518
|
|
- #construct datas and check consistency
|
519
|
|
- #@todo implementation (waiting for LeApi to be plugged to LeQuery)
|
520
|
|
- def __fetch_construct_check_update(self, filters, rel_filters, datas):
|
521
|
|
- """
|
522
|
|
- instances = self._target_class.get(filters, rel_filters)
|
523
|
|
- for instance in instances:
|
524
|
|
- instance.check_datas_value(instance.
|
525
|
|
- """
|
526
|
|
- pass
|
527
|
|
-
|
|
492
|
+ ##@brief Instanciate an update query
|
|
493
|
+ #
|
|
494
|
+ #If a class and not an instance is given, no query_filters are expected
|
|
495
|
+ #and the update will be fast and simple. Else we have to run a get query
|
|
496
|
+ #before updating (to fetch datas, update them and then, construct them
|
|
497
|
+ #and check their consistency)
|
|
498
|
+ #@param target LeObject clas or instance
|
|
499
|
+ #@param query_filters list|None
|
|
500
|
+ #@todo change strategy with instance update. We have to accept datas for
|
|
501
|
+ #the execute method
|
|
502
|
+ def __init__(self, target, query_filters = None):
|
|
503
|
+ ##@brief This attr is set only if the target argument is an
|
|
504
|
+ #instance of a LeObject subclass
|
|
505
|
+ self.__leobject_datas = None
|
|
506
|
+ target_class = target
|
|
507
|
+ if not inspect.isclass(target):
|
|
508
|
+ if query_filters is not None:
|
|
509
|
+ msg = "No query_filters accepted when an instance is given as \
|
|
510
|
+target to LeUpdateQuery constructor"
|
|
511
|
+ raise AttributeError(msg)
|
|
512
|
+ target_class = target.__class__
|
|
513
|
+ if self.initialized():
|
|
514
|
+ self.__leobject_instance_datas = target.datas()
|
|
515
|
+ else:
|
|
516
|
+ filters = [(target._uid[0], '=', target.uid())]
|
|
517
|
+
|
|
518
|
+ super().__init__(target_class, query_filters)
|
528
|
519
|
|
529
|
520
|
##@brief Implements an update query
|
530
|
521
|
#@param filters list : see @ref LeFilteredQuery
|
531
|
522
|
#@param rel_filters list : see @ref LeFilteredQuery
|
532
|
523
|
#@param datas dict : datas to update
|
533
|
524
|
#@returns the number of updated items
|
|
525
|
+ #@todo change stategy for instance update. Datas should be allowed
|
|
526
|
+ #for execute method (and query)
|
534
|
527
|
def __query(self, filters, rel_filters, datas):
|
535
|
|
- self.__fetch_construct_check_update(filters, rel_filters, datas)
|
536
|
|
- nb_updated = self._rw_datasource.update(
|
537
|
|
- self._target_class, filters, rel_filters, datas)
|
|
528
|
+ uid_name = self._target_class._uid[0]
|
|
529
|
+ if self.__leobject_instance is not None:
|
|
530
|
+ #Instance update
|
|
531
|
+ #Building query_filter
|
|
532
|
+ filters = [(
|
|
533
|
+ uid_name, '=', self.__leobject_instance_datas[uid_name])]
|
|
534
|
+ self._rw_datasource.update(
|
|
535
|
+ self._target_class, filters, [],
|
|
536
|
+ self.__leobject_instance_datas)
|
|
537
|
+ else:
|
|
538
|
+ #Update by filters, we have to fetch datas before updating
|
|
539
|
+ res = self._ro_datasource.select(
|
|
540
|
+ self._target_class, self._target_class.fieldnames(True),
|
|
541
|
+ filters, rel_filters)
|
|
542
|
+ #Checking and constructing datas
|
|
543
|
+ upd_datas = dict()
|
|
544
|
+ for res_data in res:
|
|
545
|
+ res_data.update(datas)
|
|
546
|
+ res_datas = self._target_class.prepare_datas(
|
|
547
|
+ res_data, True, True)
|
|
548
|
+ filters = [(uid_name, '=', res_data[uid_name])]
|
|
549
|
+ self._rw_datasource.update(
|
|
550
|
+ self._target_class, filters, [],
|
|
551
|
+ res_datas)
|
538
|
552
|
return nb_updated
|
539
|
553
|
|
540
|
554
|
## @brief Execute the update query
|
541
|
|
- def execute(self, datas):
|
|
555
|
+ def execute(self, datas = None):
|
|
556
|
+ if self.__leobject_instance is not None and datas is not None:
|
|
557
|
+ raise AttributeError("No datas expected when running an update \
|
|
558
|
+query on an instance")
|
542
|
559
|
return super().execute(datas = datas)
|
543
|
560
|
|
544
|
561
|
##@brief A query to delete an object
|
|
@@ -639,7 +656,7 @@ class LeGetQuery(LeFilteredQuery):
|
639
|
656
|
err_l[fieldname] = expt
|
640
|
657
|
if len(err_l) > 0:
|
641
|
658
|
msg = "Error while setting field_list in a get query"
|
642
|
|
- raise LeQueryError(msg = msg, exceptions = err_l)
|
|
659
|
+ raise LeApiQueryErrors(msg = msg, exceptions = err_l)
|
643
|
660
|
self.__field_list = list(set(field_list))
|
644
|
661
|
|
645
|
662
|
##@brief Execute the get query
|