No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

query.py 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. #-*- coding: utf-8 -*-
  2. import re
  3. from .leobject import LeObject, LeApiErrors, LeApiDataCheckError
  4. from lodel.plugin.hooks import LodelHook
  5. from lodel import logger
  6. class LeQueryError(Exception):
  7. ##@brief Instanciate a new exceptions handling multiple exceptions
  8. #@param msg str : Exception message
  9. #@param exceptions dict : A list of data check Exception with concerned
  10. # field (or stuff) as key
  11. def __init__(self, msg = "Unknow error", exceptions = None):
  12. self._msg = msg
  13. self._exceptions = dict() if exceptions is None else exceptions
  14. def __repr__(self):
  15. return self.__str__()
  16. def __str__(self):
  17. msg = self._msg
  18. if isinstance(self._exceptions, dict):
  19. for_iter = self._exceptions.items()
  20. else:
  21. for_iter = enumerate(self.__exceptions)
  22. for obj, expt in for_iter:
  23. msg += "\n\t{expt_obj} : ({expt_name}) {expt_msg}; ".format(
  24. expt_obj = obj,
  25. expt_name=expt.__class__.__name__,
  26. expt_msg=str(expt)
  27. )
  28. return msg
  29. class LeQuery(object):
  30. ##@brief Hookname prefix
  31. _hook_prefix = None
  32. ##@brief arguments for the LeObject.check_data_value()
  33. _data_check_args = { 'complete': False, 'allow_internal': False }
  34. ##@brief Abstract constructor
  35. # @param target_class LeObject : class of object the query is about
  36. def __init__(self, target_class):
  37. if self._hook_prefix is None:
  38. raise NotImplementedError("Abstract class")
  39. if not issubclass(target_class, LeObject):
  40. raise TypeError("target class has to be a child class of LeObject")
  41. self._target_class = target_class
  42. self._datasource = target_class._datasource
  43. ##@brief Execute a query and return the result
  44. # @param **datas
  45. # @return the query result
  46. # @see LeQuery.__query()
  47. #
  48. def execute(self, datas = None):
  49. if len(datas) > 0:
  50. self._target_class.check_datas_value(
  51. datas,
  52. **self._data_check_args)
  53. self._target_class.prepare_datas() #not yet implemented
  54. if self._hook_prefix is None:
  55. raise NotImplementedError("Abstract method")
  56. LodelHook.call_hook( self._hook_prefix+'_pre',
  57. self._target_class,
  58. datas)
  59. ret = self.__query(self._target_class._datasource, **datas)
  60. ret = LodelHook.call_hook( self._hook_prefix+'_post',
  61. self._target_class,
  62. ret)
  63. return ret
  64. ##@brief Childs classes implements this method to execute the query
  65. # @param **datas
  66. # @return query result
  67. def __query(self, **datas):
  68. raise NotImplementedError("Asbtract method")
  69. ##@return a dict with query infos
  70. def dump_infos(self):
  71. return {'target_class': self._target_class}
  72. def __repr__(self):
  73. ret = "<{classname} target={target_class}>"
  74. return ret.format(
  75. classname=self.__class__.__name__,
  76. target_class = self._target_class)
  77. ##@brief Abstract class handling query with filters
  78. #
  79. #@todo add handling of inter-datasource queries
  80. #
  81. #@warning relationnal filters on multiple classes from different datasource
  82. # will generate a lot of subqueries
  83. class LeFilteredQuery(LeQuery):
  84. ##@brief The available operators used in query definitions
  85. _query_operators = [
  86. ' = ',
  87. ' <= ',
  88. ' >= ',
  89. ' != ',
  90. ' < ',
  91. ' > ',
  92. ' in ',
  93. ' not in ',
  94. ' like ',
  95. ' not like ']
  96. ##@brief Regular expression to process filters
  97. _query_re = None
  98. ##@brief Abtract constructor for queries with filter
  99. #@param target_class LeObject : class of object the query is about
  100. #@param query_filters list : with a tuple (only one filter) or a list of
  101. # tuple or a dict: {OP,list(filters)} with OP = 'OR' or 'AND for tuple
  102. # (FIELD,OPERATOR,VALUE)
  103. def __init__(self, target_class, query_filters = None):
  104. super().__init__(target_class)
  105. ##@brief The query filter tuple(std_filter, relational_filters)
  106. self.__query_filter = None
  107. ##@brief Stores potential subqueries (used when a query implies
  108. # more than one datasource.
  109. #
  110. # Subqueries are tuple(target_class_ref_field, LeGetQuery)
  111. self.subqueries = None
  112. self.set_query_filter(query_filters)
  113. ##@brief Abstract FilteredQuery execution method
  114. #
  115. # This method takes care to execute subqueries before calling super execute
  116. def execute(self, datas = None):
  117. #copy originals filters
  118. orig_filters = copy.copy(self.__query_filter)
  119. std_filters, rel_filters = self.__query_filter
  120. for rfield, subq in self.subqueries:
  121. subq_res = subq.execute()
  122. std_filters.append(
  123. (rfield, ' in ', subq_res))
  124. self.__query_filter = (std_filters, rel_filters)
  125. try:
  126. res = super().execute()
  127. except Exception as e:
  128. #restoring filters even if an exception is raised
  129. self.__query_filter = orig_filter
  130. raise e #reraise
  131. #restoring filters
  132. self.__query_filter = orig_filters
  133. return res
  134. ##@brief Add filter(s) to the query
  135. #
  136. # This method is also able to slice query if different datasources are
  137. # implied in the request
  138. #
  139. #@param query_filter list|tuple|str : A single filter or a list of filters
  140. #@see LeFilteredQuery._prepare_filters()
  141. def set_query_filter(self, query_filter):
  142. if isinstance(query_filter, str):
  143. query_filter = [query_filter]
  144. #Query filter prepration
  145. filters_orig , rel_filters = self._prepare_filters(query_filter)
  146. # Here we now that each relational filter concern only one datasource
  147. # thank's to _prepare_relational_fields
  148. #Multiple datasources detection
  149. self_ds_name = self._target_class._datasource_name
  150. result_rel_filters = list() # The filters that will stay in the query
  151. other_ds_filters = dict()
  152. for rfilter in rel_filters:
  153. (rfield, ref_dict), op, value = rfilter
  154. #rfield : the field in self._target_class
  155. tmp_rel_filter = dict() #designed to stores rel_field of same DS
  156. # First step : simplification
  157. # Trying to delete relational filters done on referenced class uid
  158. for tclass, tfield in ref_dict.items():
  159. #tclass : reference target class
  160. #tfield : referenced field from target class
  161. if tfield == tclass.uid_fieldname:
  162. #This relational filter can be simplified as
  163. # ref_field, op, value
  164. # Note : we will have to dedup filters_orig
  165. filters_orig.append((rfield, op, value))
  166. del(ref_dict[tclass])
  167. #Determine what to do with other relational filters given
  168. # referenced class datasource
  169. #Remember : each class in a relational filter has the same
  170. # datasource
  171. tclass = list(ref_dict.keys())[0]
  172. cur_ds = tclass._datasource_name
  173. if cur_ds == self_ds_name:
  174. # Same datasource, the filter stay is self query
  175. result_rel_filters.append(((rfield, ref_dict), op, value))
  176. else:
  177. # Different datasource, we will have to create a subquery
  178. if cur_ds not in other_ds_filters:
  179. other_ds_filters[cur_ds] = list()
  180. other_ds_filters[cur_ds].append(
  181. ((rfield, ref_dict), op, value))
  182. #deduplication of std filters
  183. filters_orig = list(set(filters_orig))
  184. # Sets __query_filter attribute of self query
  185. self.__query_filter = (filters_orig, result_rel_filters)
  186. #Sub queries creation
  187. subq = list()
  188. for ds, rfilters in other_ds_filters.items():
  189. for rfilter in rfilters:
  190. (rfield, ref_dict), op, value = rfilter
  191. for tclass, tfield in ref_dict.items():
  192. query = LeGetQuery(
  193. target_class = tclass,
  194. query_filter = [(rfield, op, value)],
  195. field_list = [tfield])
  196. subq.append((rfield, query))
  197. ##@return informations
  198. def dump_infos(self):
  199. ret = super().dump_infos()
  200. ret['query_filter'] = self.__query_filter
  201. return ret
  202. def __repr__(self):
  203. ret = "<{classname} target={target_class} query_filter={query_filter}>"
  204. return ret.format(
  205. classname=self.__class__.__name__,
  206. query_filter = self.__query_filter,
  207. target_class = self._target_class)
  208. ## @brief Prepare filters for datasource
  209. #
  210. #A filter can be a string or a tuple with len = 3.
  211. #
  212. #This method divide filters in two categories :
  213. #
  214. #@par Simple filters
  215. #
  216. #Those filters concerns fields that represent object values (a title,
  217. #the content, etc.) They are composed of three elements : FIELDNAME OP
  218. # VALUE . Where :
  219. #- FIELDNAME is the name of the field
  220. #- OP is one of the authorized comparison operands ( see
  221. #@ref LeFilteredQuery.query_operators )
  222. #- VALUE is... a value
  223. #
  224. #@par Relational filters
  225. #
  226. #Those filters concerns on reference fields ( see the corresponding
  227. #abstract datahandler @ref lodel.leapi.datahandlers.base_classes.Reference)
  228. #The filter as quite the same composition than simple filters :
  229. # FIELDNAME[.REF_FIELD] OP VALUE . Where :
  230. #- FIELDNAME is the name of the reference field
  231. #- REF_FIELD is an optionnal addon to the base field. It indicate on wich
  232. #field of the referenced object the comparison as to be done. If no
  233. #REF_FIELD is indicated the comparison will be done on identifier.
  234. #
  235. #@param cls
  236. #@param filters_l list : This list of str or tuple (or both)
  237. #@return a tuple(FILTERS, RELATIONNAL_FILTERS
  238. #@todo move this doc in another place (a dedicated page ?)
  239. def _prepare_filters(self, filters_l):
  240. filters = list()
  241. res_filters = list()
  242. rel_filters = list()
  243. err_l = dict()
  244. #Splitting in tuple if necessary
  245. for i,fil in enumerate(filters_l):
  246. if len(fil) == 3 and not isinstance(fil, str):
  247. filters.append(tuple(fil))
  248. else:
  249. try:
  250. filters.append(self.split_filter(fil))
  251. except ValueError as e:
  252. err_l["filter %d" % i] = e
  253. for field, operator, value in filters:
  254. err_key = "%s %s %s" % (field, operator, value) #to push in err_l
  255. # Spliting field name to be able to detect a relational field
  256. field_spl = field.split('.')
  257. if len(field_spl) == 2:
  258. field, ref_field = field_spl
  259. elif len(field_spl) == 1:
  260. ref_field = None
  261. else:
  262. err_l[field] = NameError( "'%s' is not a valid relational \
  263. field name" % fieldname)
  264. continue
  265. # Checking field against target_class
  266. ret = self._check_field(self._target_class, field)
  267. if isinstance(ret, Exception):
  268. err_l[field] = ret
  269. continue
  270. field_datahandler = self._target_class.field(field)
  271. if ref_field is not None and not field_datahandler.is_reference():
  272. # inconsistency
  273. err_l[field] = NameError( "The field '%s' in %s is not \
  274. a relational field, but %s.%s was present in the filter"
  275. % ( field,
  276. self._target_class.__name__,
  277. field,
  278. ref_field))
  279. if field_datahandler.is_reference():
  280. #Relationnal field
  281. if ref_field is None:
  282. # ref_field default value
  283. ref_uid = set(
  284. [lc._uid for lc in field_datahandler.linked_classes])
  285. if len(ref_uid) == 1:
  286. ref_field = ref_uid[0]
  287. else:
  288. if len(ref_uid) > 1:
  289. msg = "The referenced classes are identified by \
  290. fields with different name. Unable to determine wich field to use for the \
  291. reference"
  292. else:
  293. msg = "Unknow error when trying to determine wich \
  294. field to use for the relational filter"
  295. err_l[err_key] = RuntimeError(msg)
  296. continue
  297. # Prepare relational field
  298. ret = self._prepare_relational_fields(field, ref_field)
  299. if isinstance(ret, Exception):
  300. err_l[err_key] = ret
  301. continue
  302. else:
  303. rel_filters.append((ret, operator, value))
  304. else:
  305. res_filters.append((field,operator, value))
  306. if len(err_l) > 0:
  307. raise LeApiDataCheckError(
  308. "Error while preparing filters : ",
  309. err_l)
  310. return (res_filters, rel_filters)
  311. ## @brief Check and split a query filter
  312. # @note The query_filter format is "FIELD OPERATOR VALUE"
  313. # @param query_filter str : A query_filter string
  314. # @param cls
  315. # @return a tuple (FIELD, OPERATOR, VALUE)
  316. @classmethod
  317. def split_filter(cls, query_filter):
  318. if cls._query_re is None:
  319. cls.__compile_query_re()
  320. matches = cls._query_re.match(query_filter)
  321. if not matches:
  322. msg = "The query_filter '%s' seems to be invalid"
  323. raise ValueError(msg % query_filter)
  324. result = (
  325. matches.group('field'),
  326. re.sub(r'\s', ' ', matches.group('operator'), count=0),
  327. matches.group('value').strip())
  328. result = [r.strip() for r in result]
  329. for r in result:
  330. if len(r) == 0:
  331. msg = "The query_filter '%s' seems to be invalid"
  332. raise ValueError(msg % query_filter)
  333. return result
  334. ## @brief Compile the regex for query_filter processing
  335. # @note Set _LeObject._query_re
  336. @classmethod
  337. def __compile_query_re(cls):
  338. op_re_piece = '(?P<operator>(%s)'
  339. op_re_piece %= cls._query_operators[0].replace(' ', '\s')
  340. for operator in cls._query_operators[1:]:
  341. op_re_piece += '|(%s)'%operator.replace(' ', '\s')
  342. op_re_piece += ')'
  343. re_full = '^\s*(?P<field>([a-z_][a-z0-9\-_]*\.)?[a-z_][a-z0-9\-_]*)\s*'
  344. re_full += op_re_piece+'\s*(?P<value>.*)\s*$'
  345. cls._query_re = re.compile(re_full, flags=re.IGNORECASE)
  346. pass
  347. @classmethod
  348. def _check_field(cls, target_class, fieldname):
  349. try:
  350. target_class.field(fieldname)
  351. except NameError:
  352. tc_name = target_class.__name__
  353. return ValueError("No such field '%s' in %s" % ( fieldname,
  354. tc_name))
  355. ##@brief Prepare a relational filter
  356. #
  357. #Relational filters are composed of a tuple like the simple filters
  358. #but the first element of this tuple is a tuple to :
  359. #
  360. #<code>( (FIELDNAME, {REF_CLASS: REF_FIELD}), OP, VALUE)</code>
  361. # Where :
  362. #- FIELDNAME is the field name is the target class
  363. #- the second element is a dict with :
  364. # - REF_CLASS as key. It's a LeObject child class
  365. # - REF_FIELD as value. The name of the referenced field in the REF_CLASS
  366. #
  367. #Visibly the REF_FIELD value of the dict will vary only when
  368. #no REF_FIELD is explicitly given in the filter string notation
  369. #and REF_CLASSES has differents uid
  370. #
  371. #@par String notation examples
  372. #<pre>contributeur IN (1,2,3,5)</pre> will be transformed into :
  373. #<pre>(
  374. # (
  375. # contributeur,
  376. # {
  377. # auteur: 'lodel_id',
  378. # traducteur: 'lodel_id'
  379. # }
  380. # ),
  381. # ' IN ',
  382. # [ 1,2,3,5 ])</pre>
  383. #@todo move the documentation to another place
  384. #
  385. #@param fieldname str : The relational field name
  386. #@param ref_field str|None : The referenced field name (if None use
  387. #uniq identifiers as referenced field
  388. #@return a well formed relational filter tuple or an Exception instance
  389. def _prepare_relational_fields(self, fieldname, ref_field = None):
  390. datahandler = self._target_class.field(fieldname)
  391. # now we are going to fetch the referenced class to see if the
  392. # reference field is valid
  393. ref_classes = datahandler.linked_classes
  394. ref_dict = dict()
  395. if ref_field is None:
  396. for ref_class in ref_classes:
  397. ref_dict[ref_class] = ref_class.uid_fieldname
  398. else:
  399. r_ds = None
  400. for ref_class in ref_classes:
  401. if r_ds is None:
  402. r_ds = ref_class._datasource_name
  403. elif ref_class._datasource_name != r_ds:
  404. return RuntimeError("All referenced class doesn't have the same datasource. Query not possible")
  405. if ref_field in ref_class.fieldnames(True):
  406. ref_dict[ref_class] = ref_field
  407. else:
  408. logger.debug("Warning the class %s is not considered in \
  409. the relational filter %s" % ref_class.__name__)
  410. if len(ref_dict) == 0:
  411. return NameError( "No field named '%s' in referenced objects"
  412. % ref_field)
  413. return (fieldname, ref_dict)
  414. ##@brief A query to insert a new object
  415. class LeInsertQuery(LeQuery):
  416. _hook_prefix = 'leapi_insert_'
  417. _data_check_args = { 'complete': True, 'allow_internal': False }
  418. def __init__(self, target_class):
  419. super().__init__(target_class)
  420. ## @brief Implements an insert query operation, with only one insertion
  421. # @param **datas : datas to be inserted
  422. def __query(self, **datas):
  423. nb_inserted = self._datasource.insert(self._target_class,**datas)
  424. if nb_inserted < 0:
  425. raise LeQueryError("Insertion error")
  426. return nb_inserted
  427. """
  428. ## @brief Implements an insert query operation, with multiple insertions
  429. # @param datas : list of **datas to be inserted
  430. def __query(self, datas):
  431. nb_inserted = self._datasource.insert_multi(
  432. self._target_class,datas_list)
  433. if nb_inserted < 0:
  434. raise LeQueryError("Multiple insertions error")
  435. return nb_inserted
  436. """
  437. ## @brief Execute the insert query
  438. def execute(self, **datas):
  439. return super().execute(**datas)
  440. ##@brief A query to update datas for a given object
  441. class LeUpdateQuery(LeFilteredQuery):
  442. _hook_prefix = 'leapi_update_'
  443. _data_check_args = { 'complete': True, 'allow_internal': False }
  444. def __init__(self, target_class, query_filter):
  445. super().__init__(target_class, query_filter)
  446. ##@brief Implements an update query
  447. # @param **datas : datas to update
  448. # @returns the number of updated items
  449. # @exception when the number of updated items is not as expected
  450. def __query(self, **datas):
  451. # select _uid corresponding to query_filter
  452. l_uids=self._datasource.select( self._target_class,
  453. list(self._target_class.getuid()),
  454. query_filter,
  455. None,
  456. None,
  457. None,
  458. None,
  459. 0,
  460. False)
  461. # list of dict l_uids : _uid(s) of the objects to be updated,
  462. # corresponding datas
  463. nb_updated = self._datasource.update( self._target_class,
  464. l_uids,
  465. **datas)
  466. if nb_updated != len(l_uids):
  467. msg = "Number of updated items: %d is not as expected: %d "
  468. msg %= (nb_updated, len(l_uids))
  469. raise LeQueryError(msg)
  470. return nb_updated
  471. ## @brief Execute the update query
  472. def execute(self, **datas):
  473. return super().execute(**datas)
  474. ##@brief A query to delete an object
  475. class LeDeleteQuery(LeFilteredQuery):
  476. _hook_prefix = 'leapi_delete_'
  477. def __init__(self, target_class, query_filter):
  478. super().__init__(target_class, query_filter)
  479. ## @brief Execute the delete query
  480. def execute(self):
  481. return super().execute()
  482. ##@brief Implements delete query operations
  483. # @returns the number of deleted items
  484. # @exception when the number of deleted items is not as expected
  485. def __query(self):
  486. # select _uid corresponding to query_filter
  487. l_uids = self._datasource.select( self._target_class,
  488. list(self._target_class.getuid()),
  489. query_filter,
  490. None,
  491. None,
  492. None,
  493. None,
  494. 0,
  495. False)
  496. # list of dict l_uids : _uid(s) of the objects to be deleted
  497. nb_deleted = datasource.update(self._target_class,l_uids, **datas)
  498. if nb_deleted != len(l_uids):
  499. msg = "Number of deleted items %d is not as expected %d "
  500. msg %= (nb_deleted, len(l_uids))
  501. raise LeQueryError(msg)
  502. return nb_deleted
  503. class LeGetQuery(LeFilteredQuery):
  504. _hook_prefix = 'leapi_get_'
  505. ##@brief Instanciate a new get query
  506. #@param target_class LeObject : class of object the query is about
  507. #@param query_filters dict : {OP, list of query filters }
  508. # or tuple (FIELD, OPERATOR, VALUE) )
  509. #@param field_list list|None : list of string representing fields see
  510. # @ref leobject_filters
  511. #@param order list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
  512. #@param group list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
  513. #@param limit int : The maximum number of returned results
  514. #@param offset int : offset
  515. def __init__(self, target_class, query_filter, **kwargs):
  516. super().__init__(target_class, query_filter)
  517. ##@brief The fields to get
  518. self.__field_list = None
  519. ##@brief An equivalent to the SQL ORDER BY
  520. self.__order = None
  521. ##@brief An equivalent to the SQL GROUP BY
  522. self.__group = None
  523. ##@brief An equivalent to the SQL LIMIT x
  524. self.__limit = None
  525. ##@brief An equivalent to the SQL LIMIT x, OFFSET
  526. self.__offset = 0
  527. # Checking kwargs and assigning default values if there is some
  528. for argname in kwargs:
  529. if argname not in (
  530. 'field_list', 'order', 'group', 'limit', 'offset'):
  531. raise TypeError("Unexpected argument '%s'" % argname)
  532. if 'field_list' not in kwargs:
  533. self.set_field_list(target_class.fieldnames(include_ro = True))
  534. else:
  535. self.set_field_list(kwargs['field_list'])
  536. if 'order' in kwargs:
  537. #check kwargs['order']
  538. self.__order = kwargs['order']
  539. if 'group' in kwargs:
  540. #check kwargs['group']
  541. self.__group = kwargs['group']
  542. if 'limit' in kwargs:
  543. try:
  544. self.__limit = int(kwargs[limit])
  545. if self.__limit <= 0:
  546. raise ValueError()
  547. except ValueError:
  548. msg = "limit argument expected to be an interger > 0"
  549. raise ValueError(msg)
  550. if 'offset' in kwargs:
  551. try:
  552. self.__offset = int(kwargs['offset'])
  553. if self.__offset < 0:
  554. raise ValueError()
  555. except ValueError:
  556. msg = "offset argument expected to be an integer >= 0"
  557. raise ValueError(msg)
  558. ##@brief Set the field list
  559. # @param field_list list | None : If None use all fields
  560. # @return None
  561. # @throw LeQueryError if unknown field given
  562. def set_field_list(self, field_list):
  563. err_l = dict()
  564. for fieldname in field_list:
  565. ret = self._check_field(self._target_class, fieldname)
  566. if isinstance(ret, Exception):
  567. msg = "No field named '%s' in %s"
  568. msg %= (fieldname, self._target_class.__name__)
  569. expt = NameError(msg)
  570. err_l[fieldname] = expt
  571. if len(err_l) > 0:
  572. msg = "Error while setting field_list in a get query"
  573. raise LeQueryError(msg = msg, exceptions = err_l)
  574. self.__field_list = list(set(field_list))
  575. ##@brief Execute the get query
  576. def execute(self):
  577. return super().execute()
  578. ##@brief Implements select query operations
  579. # @returns a list containing the item(s)
  580. def __query(self, datasource):
  581. # select datas corresponding to query_filter
  582. l_datas=datasource.select( self._target_class,
  583. list(self.field_list),
  584. self.query_filter,
  585. None,
  586. self.__order,
  587. self.__group,
  588. self.__limit,
  589. self.offset,
  590. False)
  591. return l_datas
  592. ##@return a dict with query infos
  593. def dump_infos(self):
  594. ret = super().dump_infos()
  595. ret.update( { 'field_list' : self.__field_list,
  596. 'order' : self.__order,
  597. 'group' : self.__group,
  598. 'limit' : self.__limit,
  599. 'offset': self.__offset,
  600. })
  601. return ret
  602. def __repr__(self):
  603. ret = "<LeGetQuery target={target_class} filter={query_filter} \
  604. field_list={field_list} order={order} group={group} limit={limit} \
  605. offset={offset}>"
  606. return ret.format(**self.dump_infos())