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.

ledatasourcesql.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #-*- coding: utf-8 -*-
  2. import pymysql
  3. from leobject.datasources.dummy import DummyDatasource
  4. from leobject.leobject import REL_SUB, REL_SUP
  5. from mosql.db import Database, all_to_dicts
  6. from mosql.query import select, insert, update, delete, join
  7. from mosql.util import raw, or_
  8. import mosql.mysql
  9. from DataSource.MySQL.MySQL import MySQL
  10. ## MySQL DataSource for LeObject
  11. class LeDataSourceSQL(DummyDatasource):
  12. RELATIONS_POSITIONS_FIELDS = {REL_SUP: 'superior_id', REL_SUB: 'subordinate_id'}
  13. def __init__(self, module=pymysql, conn_args=None):
  14. super(LeDataSourceSQL, self).__init__()
  15. self.module = module
  16. self.datasource_utils = MySQL
  17. if conn_args is None:
  18. conn_args = self.datasource_utils.connections['default']
  19. self.connection = Database(self.module, host=conn_args['host'], user=conn_args['user'], passwd=conn_args['passwd'], db=conn_args['db'])
  20. ## @brief inserts a new object
  21. # @param letype LeType
  22. # @param leclass LeClass
  23. # @param datas dict : dictionnary of field:value pairs to save
  24. # @return int : lodel_id of the created object
  25. # @todo add the returning clause and the insertion in "object"
  26. def insert(self, letype, leclass, datas):
  27. if isinstance(datas, list):
  28. res = list()
  29. for data in datas:
  30. res.append(self.insert(letype, leclass, data))
  31. return res
  32. elif isinstance(datas, dict):
  33. with self.connection as cur:
  34. object_datas = {'class_id': leclass._class_id, 'type_id': letype._type_id}
  35. if cur.execute(insert(self.datasource_utils.objects_table_name, object_datas)) != 1:
  36. raise RuntimeError('SQL error')
  37. if cur.execute('SELECT last_insert_id() as lodel_id') != 1:
  38. raise RuntimeError('SQL error')
  39. lodel_id, = cur.fetchone()
  40. datas[self.datasource_utils.field_lodel_id] = lodel_id
  41. query_table_name = self.datasource_utils.get_table_name_from_class(leclass.__name__)
  42. query = insert(query_table_name, datas)
  43. if cur.execute(query) != 1:
  44. raise RuntimeError('SQL error')
  45. return lodel_id
  46. ## @brief search for a collection of objects
  47. # @param leclass LeClass
  48. # @param letype LeType
  49. # @field_list list
  50. # @param filters list : list of tuples formatted as (FIELD, OPERATOR, VALUE)
  51. # @param relation_filters list : list of tuples formatted as (('superior'|'subordinate', FIELD), OPERATOR, VALUE)
  52. # @return list
  53. def get(self, leclass, letype, field_list, filters, relational_filters=None):
  54. query_table_name = self.datasource_utils.get_table_name_from_class(leclass.__name__)
  55. where_filters = self._prepare_filters(filters, query_table_name)
  56. join_fields = {}
  57. if relational_filters is not None and len(relational_filters) > 0:
  58. rel_filters = self._prepare_rel_filters(relational_filters)
  59. for rel_filter in rel_filters:
  60. # join condition
  61. relation_table_join_field = "%s.%s" % (self.datasource_utils.relations_table_name, self.RELATIONS_POSITIONS_FIELDS[rel_filter['position']])
  62. query_table_join_field = "%s.%s" % (query_table_name, self.datasource_utils.field_lodel_id)
  63. join_fields[query_table_join_field] = relation_table_join_field
  64. # Adding "where" filters
  65. where_filters['%s.%s' % (self.datasource_utils.relations_table_name, self.datasource_utils.relations_field_nature)] = rel_filter['nature']
  66. where_filters[rel_filter['condition_key']] = rel_filter['condition_value']
  67. # building the query
  68. query = select(query_table_name, where=where_filters, select=field_list, joins=join(self.datasource_utils.relations_table_name, join_fields))
  69. else:
  70. query = select(query_table_name, where=where_filters, select=field_list)
  71. # Executing the query
  72. with self.connection as cur:
  73. results = all_to_dicts(cur.execute(query))
  74. return results
  75. ## @brief delete an existing object
  76. # @param letype LeType
  77. # @param leclass LeClass
  78. # @param filters list : list of tuples formatted as (FIELD, OPERATOR, VALUE)
  79. # @param relational_filters list : list of tuples formatted as (('superior'|'subordinate', FIELD), OPERATOR, VALUE)
  80. # @return bool : True on success
  81. def delete(self, letype, leclass, filters, relational_filters):
  82. query_table_name = self.datasource_utils.get_table_name_from_class(leclass.__name__)
  83. prep_filters = self._prepare_filters(filters, query_table_name)
  84. prep_rel_filters = self._prepare_rel_filters(relational_filters)
  85. if len(prep_rel_filters) > 0:
  86. query = "DELETE %s FROM " % query_table_name
  87. for prep_rel_filter in prep_rel_filters:
  88. query += "%s INNER JOIN %s ON (%s.%s = %s.%s)" % (
  89. self.datasource_utils.relations_table_name,
  90. query_table_name,
  91. self.datasource_utils.relations_table_name,
  92. prep_rel_filter['position'],
  93. query_table_name,
  94. self.datasource_utils.field_lodel_id
  95. )
  96. if prep_rel_filter['condition_key'][0] is not None:
  97. prep_filters[("%s.%s" % (self.datasource_utils.relations_table_name, prep_rel_filter['condition_key'][0]), prep_rel_filter['condition_key'][1])] = prep_rel_filter['condition_value']
  98. if prep_filters is not None and len(prep_filters) > 0:
  99. query += " WHERE "
  100. filter_counter = 0
  101. for filter_item in prep_filters:
  102. if filter_counter > 1:
  103. query += " AND "
  104. query += "%s %s %s" % (filter_item[0][0], filter_item[0][1], filter_item[1])
  105. else:
  106. query = delete(query_table_name, filters)
  107. query_delete_from_object = delete(self.datasource_utils.objects_table_name, {'lodel_id': filters['lodel_id']})
  108. with self.connection as cur:
  109. cur.execute(query)
  110. cur.execute(query_delete_from_object)
  111. return True
  112. ## @brief update an existing object's data
  113. # @param letype LeType
  114. # @param leclass LeClass
  115. # @param filters list : list of tuples formatted as (FIELD, OPERATOR, VALUE)
  116. # @param rel_filters list : list of tuples formatted as (('superior'|'subordinate', FIELD), OPERATOR, VALUE)
  117. # @param data dict
  118. # @return bool
  119. # @todo prendre en compte les rel_filters
  120. def update(self, letype, leclass, filters, rel_filters, data):
  121. query_table_name = self.datasource_utils.get_table_name_from_class(leclass.__name__)
  122. where_filters = filters
  123. set_data = data
  124. prepared_rel_filters = self._prepare_rel_filters(rel_filters)
  125. # Building the query
  126. query = update(table=query_table_name, where=where_filters, set=set_data)
  127. # Executing the query
  128. with self.connection as cur:
  129. cur.execute(query)
  130. return True
  131. ## @brief prepares the relational filters
  132. # @params rel_filters : (("superior"|"subordinate"), operator, value)
  133. # @return list
  134. def _prepare_rel_filters(self, rel_filters):
  135. prepared_rel_filters = []
  136. if rel_filters is not None and len(rel_filters) > 0:
  137. for rel_filter in rel_filters:
  138. rel_filter_dict = {
  139. 'position': REL_SUB if rel_filter[0][0] == REL_SUP else REL_SUB,
  140. 'nature': rel_filter[0][1],
  141. 'condition_key': (self.RELATIONS_POSITIONS_FIELDS[rel_filter[0][0]], rel_filter[1]),
  142. 'condition_value': rel_filter[2]
  143. }
  144. prepared_rel_filters.append(rel_filter_dict)
  145. return prepared_rel_filters
  146. ## @brief prepares the filters to be used by the mosql library's functions
  147. # @params filters : (FIELD, OPERATOR, VALUE) tuples
  148. # @return dict : Dictionnary with (FIELD, OPERATOR):VALUE style elements
  149. def _prepare_filters(self, filters, tablename=None):
  150. prepared_filters = {}
  151. if filters is not None and len(filters) > 0:
  152. for filter_item in filters:
  153. if '.' in filter_item[0]:
  154. prepared_filter_key = (filter_item[0], filter_item[1])
  155. else:
  156. prepared_filter_key = ("%s.%s" % (tablename, filter_item[0]), filter_item[1])
  157. prepared_filter_value = filter_item[2]
  158. prepared_filters[prepared_filter_key] = prepared_filter_value
  159. return prepared_filters
  160. ## @brief Link two object given a relation nature, depth and rank
  161. # @param lesup LeObject : a LeObject
  162. # @param lesub LeObject : a LeObject
  163. # @param nature str|None : The relation nature or None if rel2type
  164. # @param rank int : a rank
  165. def add_relation(self, lesup, lesub, nature=None, depth=None, rank=None, **rel_attr):
  166. if len(rel_attr) > 0 and nature is not None:
  167. #not a rel2type but have some relation attribute
  168. raise AttributeError("No relation attributes allowed for non rel2type relations")
  169. with self.connection as cur:
  170. sql = insert(self.datasource_utils.relations_table_name, {'id_sup': lesup.lodel_id, 'id_sub': lesub.lodel_id, 'nature': nature, 'rank': rank, 'depth': depth})
  171. if cur.execute(sql) != 1:
  172. raise RuntimeError("Unknow SQL error")
  173. if len(rel_attr) > 0:
  174. #a relation table exists
  175. cur.execute('SELECT last_insert_id()')
  176. relation_id, = cur.fetchone()
  177. raise NotImplementedError()
  178. return True
  179. ## @brief Delete a link between two objects given a relation nature
  180. # @param lesup LeObject : a LeObject
  181. # @param lesub LeObject : a LeObject
  182. # @param nature str|None : The relation nature
  183. # @return bool
  184. def del_relation(self, lesup, lesub, nature=None):
  185. if lesup is None or lesub is None:
  186. raise AttributeError("Missing member(s) of the relation to delete")
  187. delete_params = {'id_sup': lesup.lodel_id, 'id_sub': lesub.lodel_id}
  188. if nature is not None:
  189. delete_params['nature'] = nature
  190. sql = delete(self.datasource_utils.relations_table_name, delete_params)
  191. with self.connection as cur:
  192. if cur.execute(sql) != 1:
  193. raise RuntimeError("Unknown SQL Error")
  194. return True
  195. ## @brief Fetch all relations concerning an object (rel2type relations)
  196. # @param leo LeType : LeType child instance
  197. # @return a list of tuple (lesup, lesub, dict_attr)
  198. def get_relations(self, leo):
  199. sql = select(self.datasource_utils.relations_table_name, where=or_(({'id_sub':leo.lodel_id},{'id_sup':leo.lodel_id})))
  200. with self.connection as cur:
  201. results = all_to_dicts(cur.execute(sql))
  202. relations = []
  203. for result in results:
  204. id_sup = result['id_sup']
  205. id_sub = result['id_sub']
  206. del result['id_sup']
  207. del result['id_sub']
  208. rel_attr = result
  209. relations.append((id_sup, id_sub, rel_attr))
  210. return relations